Slide 1

Slide 1 text

Flutter はプロダクション開発に耐 えうるのか Google Developers Expert @wasabeef_jp wasabeef CyberAgent, Inc.

Slide 2

Slide 2 text

Flutter はプロダクション開発に耐えうるのか

Slide 3

Slide 3 text

• Flutter でプロダクトを作るという判断 • モバイルアプリの開発の現状と課題 • クロスプラットフォームの期待と課題 • アプリ開発で必要な要素 • アーキテクチャ • ライブラリ • その他 今日話すこと

Slide 4

Slide 4 text

Flutter でプロダクトを作るという判断

Slide 5

Slide 5 text

• モバイルアプリ開発の現状と課題 • クロスプラットフォームの期待と課題 Flutter でプロダクトを作るという判断

Slide 6

Slide 6 text

モバイルアプリ開発の現状と課題

Slide 7

Slide 7 text

• モバイルアプリ開発は成熟期でもあるので、致命 的な課題もなく可もなく不可もない現状。 • Jetpack Compose/Swift UI などの宣言的な UI を構築できる仕組みが来年くらいから安定化され るたらまた大きな変化が起きるかもしれない。 現状

Slide 8

Slide 8 text

過去 〜 Android アプリ開発の黎明期  RetroLambda MVC VOLLEY JUICE ActionBarSherlock Timber

Slide 9

Slide 9 text

現在 〜 Android アプリ開発の成熟期  DAGGER2 MVVM FLUX Timber

Slide 10

Slide 10 text

現在 〜 Android アプリ開発の成熟期 Activity/Fragment Repository ViewModel LiveData Local Source Room/DataStore Remote Data Source Retrofit/okHttp Jetpack Jetpack Jetpack Dagger Hilt Android Studio ここでいう成熟のイメージは アーキテクチャ論争、パフォー マンス改善手法確立がひと段落 し、それらの知見が一般化した 状態であり、アプリ開発(コー ド)の簡略化・テンプレート化 に向かっていることとする。

Slide 11

Slide 11 text

Jetpack Compose などの宣言的な UI の手法を採 用したことによるパラダイムシフトと、それによる 今後の影響は…? MVVM から Flux/Redux/MVU のような状態管理 への移行もあるかもしれない。 宣言的 UI による VIEW のパラダイムシフト

Slide 12

Slide 12 text

• Android エンジニアの不足?(なんかこれ話題が今週プチバズってた?) • iOS/Android の機能仕様ずれ (SDKやライブラリの差、認識ずれ、、) • 開発期間の長期化(良くも悪くも) 課題

Slide 13

Slide 13 text

Android エンジニアの不足 ※ 事例 即戦力の中途採用は年間 1 人するのも大変な状態であり、未経験や若 手の育成を重視する方向性にシフトしつつある。 + 10 ਓ Android Λ࠾༻ʁ iOS 50 ਓ Android 40 ਓ

Slide 14

Slide 14 text

ポジショントーク(仮) iOSエンジニア or Androidエンジニア? Swift or Kotlin ? Flutter or React Native?

Slide 15

Slide 15 text

クロスプラットフォームへの期待と課題

Slide 16

Slide 16 text

• アプリ動作の安定性 (クロスプラットフォーム <<= ネイティブ) • 将来性、先行き不安 • iOS/Android で共通の UI(悪くも) • iOS/Android の新機能への追従の遅れ? • Flutter と Kotlin MPP のどちらを採用するか? 課題

Slide 17

Slide 17 text

将来性、先行き不安 開発元の企業がいかに大 企業だろうとエンジニア コミュニティに受けが良 くないと流行らないとい うことがわかりました。

Slide 18

Slide 18 text

• 世界的に200万人以上のエンジニアが Flutter を使って いて毎月10%増加している • 利用数上位5地域は インド、中国、米国、EU、ブラジル • Google Play に既に100,000個、毎月10,000個以上の Flutter アプリがアップされている 事例 https://medium.com/flutter/flutter-spring-2020-update-f723d898d7af

Slide 19

Slide 19 text

Flutter の採用事例(国内)

Slide 20

Slide 20 text

• 2018 年 6 月サービス開始 (Flutter stable 1.0 のリリースは2018年 12 月) • 担当アプリエンジニア 2 人 REQU

Slide 21

Slide 21 text

Flutter の採用事例(国外)

Slide 22

Slide 22 text

• アリババ傘下のフリマアプリ • カメラ機能で商品検索 • アプリ評価 ★ 4.6 DAU 2000万 • Flutter 事例でユーザ数が世界最大級 ༽჈ (Idle Fish)

Slide 23

Slide 23 text

iOS/Android で共通の UI(悪くも) Flutter はマテリアルデザインを採用しているの で、悪く言うと iOS っぽいデザインが損なわれてい たりすることがあります。ただ、それは開発速度の 向上のためにデザインシステムは Flutter (共通) と して考えた方いいとして思っています。

Slide 24

Slide 24 text

iOS/Android で共通の UI(悪くも) https://flutter.dev/docs/development/ui/widgets/cupertino Cupertino widgets もある程度存在するが完璧ではないので、iOS らしいデ ザインに完全に踏襲したい場合は、Flutter 以外の選択肢も視野にいれたい。

Slide 25

Slide 25 text

iOS/Android の新機能への追従の遅れ? 例えば最近の iOS 14、Android 11 への対応は Flutter 1.22.0 でも改善され ていますし、致命的には対応が遅れてないという個人的な感覚はあるが、各 OS の新 API のインターフェース公開が後追いで来るのは事実です。

Slide 26

Slide 26 text

• アプリ動作の安定性がかなり向上 (60 fps) • エンジニアのリソース節約 • 開発期間短縮 • 2020年、開発コミュニティの急速的な拡大 • iOS/Android で共通の UI(良くも) 期待

Slide 27

Slide 27 text

エンジニアのリソース節約 サーバエンジニア iOS エンジニア Web エンジニア Android エンジニア 一般的な Swift/Kotlin アプリのアサインスタイル

Slide 28

Slide 28 text

エンジニアのリソース節約 Flutter ͷΞαΠϯελΠϧ アプリエンジニア とりあえず1人以上いれば iOS/ Android アプリ両方開発可能 サーバエンジニア Web エンジニア 余剰

Slide 29

Slide 29 text

エンジニアのリソース節約 サーバエンジニア iOS エンジニア Web エンジニア Android エンジニア 一般的な Swift/Kotlin アプリのアサインスタイル それぞれ一人の場合、 コードレビューされない ケースも...

Slide 30

Slide 30 text

エンジニアのリソース節約 Flutter ͷΞαΠϯελΠϧ サーバエンジニア アプリエンジニア Web エンジニア とりあえず1人以上いれば iOS/ Android アプリ両方開発可能 コードレビューできて結果として 品質向上も。

Slide 31

Slide 31 text

Flutter と Kotlin MPP のどちらを採用するか?

Slide 32

Slide 32 text

• Google によって開発 • 2018 年 12 月リリース (BREAKING CHANGES はありえる) • Dart • ステートフルホットリロード (高速開発) • UI ツールキット(豊富なウィジェット) Flutter

Slide 33

Slide 33 text

• グラフィックライブラリ • Android, Chrome, Firefox, Blink, Flutter で採用されている • Skia は Vulkan をバックエンドエンジンとして変更可能 • Android 9 以降、デフォルトのレンダラーは Skia になってい る(8 と 9 でUIが変わったのはそのため)。Flutter にする とレイヤーが増えるもののレンダラーは同じ Skia

Slide 34

Slide 34 text

対応プラットフォーム • Windows 7, 8, 8.1, 10 • macOS 10.10.5 or later • iOS 8 or later • Android 4.1 (JellyBean) or later • Ubuntu 14.04+, Debian 8+, openSUSE 13.3+, or Fedora Linux 24+ もちろん iOS でも Windows でも動作する Skia

Slide 35

Slide 35 text

• Swift/Kotlin で良い体験をしてしまったエンジ ニアには Dart の言語実装が足りない。 (Data class, Nested Class, Enum Custom Value, Switch expression, protected, alias..) • アーキテクチャの議論が活発(良くも悪くも) • 各 SaaS の SDK が無い場合がある Flutter の難点

Slide 36

Slide 36 text

Flutter だけだと解決できない例 • 各 SaaS や広告計測系などの Flutter SDK 対応が間に合ってない 場合は自分でラッパー相当を実装する必要があり、その時は Kotlin/Swift の知識が必要になる。 • どうしても Flutter からだとコールできない OS 側の機能を使い たい時には PlatformView を使い Flutter アプリ内のウィジェッ トの一部だけをネイティブにすることは出来るが、DRM などの 一部制限があるようなものではそれも出来ない。 例

Slide 37

Slide 37 text

Flutter だけだと解決できない例 例えばこのアプリが Flutter で作ら れてるとして、動画部分に DRM で HW Secure Decoder を使っている ような場合には PlatformViewを 使ったとしても Flutter から呼び出 すのは出来ない。

Slide 38

Slide 38 text

Flutter だけだと解決できない例 Flutter から MethodChannel API 経由で iOS/Android 側で 実装された画面をまるまる表示 することが出来るデザインだと 解決が可能になる

Slide 39

Slide 39 text

Flutter <-->- iOS/Android Flutter から MethodChannel API 経由で iOS/Android 側で 実装された画面をまるごと表示 することが出来るデザインだと 解決が可能になる

Slide 40

Slide 40 text

Flutter <-->- iOS/Android ウィジェットの一部分だけを 各 OS 側のものを使うのは出来 ないが各 OS の API を呼び出 したり、画面まるごと起動した りは出来る。

Slide 41

Slide 41 text

Flutter <-->- iOS/Android Flutter (Dart code) Android (Kotlin code) iOS (Swift code) Activity を起動 ViewController を起動

Slide 42

Slide 42 text

• JetBrains によって開発 • UI の共通化はせず、ビジネスロジックの共通化 に特化している(認証、ログなどの共通化) • Kotlin で Backends for Frontends Kotlin Multiplatform

Slide 43

Slide 43 text

JetBrains の野望として はクラサバのロジックを 全て共通化すること。将 来的には UI にも手を出 しそうな情報を小出しし ている。 Kotlin Multiplatform Kotlin/LLVM Kotlin/JVM Kotlin/JVM Kotlin/JS Common code

Slide 44

Slide 44 text

• iOS エンジニアコミュニティに刺さってない • iOS のエンジニアも Kotlin と Gradle を覚えない といけない Kotlin Multiplatform の難点

Slide 45

Slide 45 text

iOS (arm32, arm64, simulator x86_64) macOS (x86_64) Android (arm32, arm64) Windows (mingw x86_64, x86) Kotlin/Native - Target Platforms Linux (x86_64, arm32, MIPS, MIPS LE, Raspberry Pi) WebAssembly (x86_64)

Slide 46

Slide 46 text

Flutter か? Kotlin MPP か? Flutter か? Kotlin MPP か?

Slide 47

Slide 47 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト?

Slide 48

Slide 48 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス?

Slide 49

Slide 49 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は?

Slide 50

Slide 50 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は? • エンジニアの技術レベル感は?

Slide 51

Slide 51 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は? • エンジニアの技術レベル感は? • 安定的な技術志向か?リスクをとった挑戦志向か?

Slide 52

Slide 52 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は? • エンジニアの技術レベル感は? • 安定的な技術志向か?リスクをとった挑戦志向か? • プロジェクト・プロダクトのスケールを強く意識するか?

Slide 53

Slide 53 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は? • エンジニアの技術レベル感は? • 安定的な技術志向か?リスクをとった挑戦志向か? • プロジェクト・プロダクトのスケールを強く意識するか? • とにかく早くリリースしたい?

Slide 54

Slide 54 text

Flutter か? Kotlin MPP か? • ゼロから作る新規プロジェクト? • 既存プロジェクトのリアーキテクチャ?リプレイス? • アプリ開発に当てれるエンジニアの人数は? • エンジニアの技術レベル感は? • 安定的な技術志向か?リスクをとった挑戦志向か? • プロジェクト・プロダクトのスケールを強く意識するか? • とにかく早くリリースしたい?

Slide 55

Slide 55 text

Flutter か? Kotlin MPP か?  Flutter   Kotlin MPP  • 新規プロジェクト • 少人数(1〜4人) • 少人数でも両プラットフォーム でブランディングの一貫性を維 持したい • とにかく早くリリースしたい • よりチャレンジしたい • 新規/既存プロジェクト • 大人数(10人〜) • iOS/Androidアプリ開発の精 通している • リスクを抑えたい • 各プラットフォームで分けて デザインを拘りたい 例 例

Slide 56

Slide 56 text

Flutter か? Kotlin MPP か? 結論 プロダクトの仕様、プロジェクトの規模、エンジニアの 能力によって様々なケースがある。という結論

Slide 57

Slide 57 text

Hello World のその先へ

Slide 58

Slide 58 text

アプリ開発で必要な要素

Slide 59

Slide 59 text

• アーキテクチャ • ライブラリ • アーキテクチャを実現するための • カスタムウィジェットを表現するための • その他 アプリ開発で必要な要素

Slide 60

Slide 60 text

Architecture

Slide 61

Slide 61 text

Architecture それぞれのアーキテクチャが目 指していることが違うので、シ ステムのライフサイクルによっ て相性の良し悪しはあります。 ただ、それが絶対的な正解はあ りません。チームの成熟度や趣 味思考を考えた結果であれば、 何を選んでも良いと思います し、自分で新たに生み出すのも 良いと思います。 MVVM Bloc Redux ELM/MVU

Slide 62

Slide 62 text

MVVM + Repository View Repository ViewModel Local Source Remote Data Source このスライドでは Android エンジニアの馴 染みが一番あるであろう MVVM + Repository を 例として取り上げていこ うと思います。 MVVM Repository

Slide 63

Slide 63 text

MVVM UI の実装において、例えばテキストを入力し、バリーデーション、データを保持し、ボタン をタップして、サーバに送信するようなコードを View に全て追加していくと UI が複雑に なっていった時には更にコードが肥大化してしまい、それは UI とプレゼンテーションロジッ クとビジネスロジックの密結合になっているのでメンテナンスが大変ですし、テストを書く が困難に思えます。 そこで必要になってくる概念が関心の分離 (SoC) です。 簡単に説明す ると全てのアーキテクチャ共通して言えることですが、何をさせたいのか?その役割によっ て分離した構成要素とすることです。

Slide 64

Slide 64 text

MVVM MVVM は Model–View–ViewModel のことを指し、その構成要素の基本形は以下となっています。 • View は UI (Widget) を描画(出力)し、ユーザからの入力データを受け取ります。 • ViewModel は View から入力された状態(データ)を適切に変換して Model として持ちます。 また、Model の状態(データ)を View 渡して画面の更新を促します。 • Model は状態(データ)を保持し、それがどう変換されて画面に描画されるかは知りません。

Slide 65

Slide 65 text

Repository Repository Local Source Remote Data Source Repository pattern はデータソース へのアクセスを抽象化するためのデ ザインパターンです。ViewModel が 持つことになりますが、ViewModel 側からすると Repository とデータ の形式だけを取り決めるだけで、入 手先がサーバからなのか、ローカル の DB なのか、オンメモリなのかな どは知りません。

Slide 66

Slide 66 text

MVVM + Repository View Repository ViewModel Local Source Remote Data Source Android で現在一般的な アーキテクチャは MVVM に Repository Pattern を合わせた設計 となっています。 MVVM Repository 

Slide 67

Slide 67 text

DI/Service Locator

Slide 68

Slide 68 text

DI/Service Locator View Repository ViewModel Local Source Remote Data Source DI/SL クラス間の密結合を減らして、 再利用性を高めるための手法。 すごくポピュラーな例をいうと テスト書きたいので、テスト実 行時はサーバ API を叩くので はなくて、モックデータを読み 込むような処理に変えたいがた めに使います。 (テスタビリティの向上)

Slide 69

Slide 69 text

主な DI/Service Locator のパッケージ  InheritedWidget   Riverpod package  • Flutter SDK に内包 • BuildContext が必要 • 子ウィジェットからでも O(1) でオブジェクトが取得できる • 少しローレベルなクラス • 有名な方が個人開発 • 新しい Provider でシンプル • まだ安定版ではない • 今や Dagger みたいな位置付 • コンパイルセーフ • Flutter に依存せずにも使える https://github.com/rrousselGit/river_pod https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html

Slide 70

Slide 70 text

Riverpod # pubspec.yaml dependencies: flutter_riverpod: ^0.11.x pubspec.yaml に依存関係を追加して、flutter pub get をする https://github.com/rrousselGit/river_pod $ flutter pub get

Slide 71

Slide 71 text

Riverpod https://github.com/rrousselGit/river_pod final myNotifierProvider = ChangeNotifierProvider((_) { return MyNotifier(); }); class MyNotifier extends ChangeNotifier { int count; /// TODO: typical ChangeNotifier logic } この例では ChangeNotifier を継承した MyNotifier を Riverpod に管理しても らいます。Riverpod には ChangerNotifierProvider というものが用意されてい るので、そこで MyNotifier のインスタンスを返却します。

Slide 72

Slide 72 text

Riverpod https://github.com/rrousselGit/river_pod main.dart にて、MyApp を Riverpod のクラスである ProviderScope で囲いま しょう。このスコープの設定がないとランタイムエラーになります。 void main() { runApp( const ProviderScope(child: MyApp()), ); }

Slide 73

Slide 73 text

Riverpod https://github.com/rrousselGit/river_pod class MainPage extends ConsumerWidget { @override Widget build(BuildContext context, ScopedReader watch) { final count = watch(myNotifierProvider); return Text(count.toString()); } } Riverpod の ConsumerWidget を継承したウィジェット内であれば、シンプル に値の監視をすることができます。MyNotifier の値が変わっていれば、この build が再実行されます。

Slide 74

Slide 74 text

State management

Slide 75

Slide 75 text

State management View Repository ViewModel Local Source Remote Data Source DI 主に View とViewModel 間 で状態の共有を行うことが 目的となっています。View 側は状態が変わったことを 知りたいので ViewModel から通知をもらうようなイ メージになります。

Slide 76

Slide 76 text

State management 右の簡単なアプリを例にします。 このアプリはカタログとカートに画面 が分かれています。各アイテムの [ADD] をタップすることで、カート にアイテムが追加される想定です。 https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

Slide 77

Slide 77 text

State management そのウィジェットツリーを表現するとこ のようになっており、それぞれクラスが 分かれていたとします。 [ADD] ボタン自体は MyListItem クラス が制御を管理しているので、どうやって MyCart クラスに通知するかを考えないと いけません。 Observable に該当する仕組みが必要で す。 https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple

Slide 78

Slide 78 text

State management https://flutter.dev/docs/development/data-and-backend/state-mgmt/simple そのウィジェットツリーを表現するとこ のようになっており、それぞれクラスが 分かれていたとします。 [ADD] ボタン自体は MyListItem クラス が制御を管理しているので、どうやって MyCart クラスに通知するかを考えないと いけません。 Observable に該当する仕組みが必要で す。

Slide 79

Slide 79 text

State management  ChangeNotifier   StateNotifier package  • Flutter SDK に内包 • 状態を複数管理できる • 割と自由にできる • その状態は Mutable • 状態の変更を通知できる • Riverpod に合わせて使える • 有名な方が個人開発 • ValueNotifier の進化版 • 状態(state)を一つだけ管理 • その状態は Immutable • 状態の変更を通知できる • Freezed で copyWith を自動 生成してもらうと良い • Riverpod に合わせて使える Android の LiveData のような役割

Slide 80

Slide 80 text

ChangeNotifier を継承した ViewModel final homeViewModelNotifierProvider = ChangeNotifierProvider( (ref) =>= HomeViewModel(repository: ref.read(newsRepositoryProvider))); class HomeViewModel extends ChangeNotifier { HomeViewModel({@required NewsRepository repository}) : _repository = repository; final NewsRepository _repository; News _news; News get news =>= _news; Future fetchNews() async { _news = _repository.getNews(); notifyListeners() } } 全体的に少しシンプルにしてありますが、HomeViewModel は ChangeNotifier を継承します。

Slide 81

Slide 81 text

ChangeNotifier を継承した ViewModel final homeViewModelNotifierProvider = ChangeNotifierProvider( (ref) =>= HomeViewModel(repository: ref.read(newsRepositoryProvider))); class HomeViewModel extends ChangeNotifier { HomeViewModel({@required NewsRepository repository}) : _repository = repository; final NewsRepository _repository; News _news; News get news =>= _news; Future fetchNews() async { _news = _repository.getNews(); notifyListeners() } } StateNotifier の場合は管理対象の状態は state を実装が必要になりますが、ChangeNotifier は特に何も。

Slide 82

Slide 82 text

ChangeNotifier を継承した ViewModel final homeViewModelNotifierProvider = ChangeNotifierProvider( (ref) =>= HomeViewModel(repository: ref.read(newsRepositoryProvider))); class HomeViewModel extends ChangeNotifier { HomeViewModel({@required NewsRepository repository}) : _repository = repository; final NewsRepository _repository; News _news; News get news =>= _news; Future fetchNews() async { _news = _repository.getNews(); notifyListeners() } } Repository で取得したデータを格納しただけでは、リスナーに通知はされません。

Slide 83

Slide 83 text

ChangeNotifier を継承した ViewModel final homeViewModelNotifierProvider = ChangeNotifierProvider( (ref) =>= HomeViewModel(repository: ref.read(newsRepositoryProvider))); class HomeViewModel extends ChangeNotifier { HomeViewModel({@required NewsRepository repository}) : _repository = repository; final NewsRepository _repository; News _news; News get news =>= _news; Future fetchNews() async { _news = _repository.getNews(); notifyListeners() } } notifyListeners() をコールすることで、通知がいきます。

Slide 84

Slide 84 text

ChangeNotifier を継承した ViewModel class MainPage extends ConsumerWidget { @override Widget build(BuildContext context, ScopedReader watch) { final news = watch(homeViewModelNotifierProvider); return Text(news.title); } } 先ほどの Riverpod の説明のところで紹介したものと同じです。

Slide 85

Slide 85 text

Networking

Slide 86

Slide 86 text

Networking View Repository ViewModel Local Source Remote Data Source DI 主に Remote Data Source でサーバの API  を叩くために Endpoint, Header, Token, Body の制 御を行います。

Slide 87

Slide 87 text

主な Http client のパッケージ  http package   dio package  • 公式に Google が開発 • メンテは活発 • シンプルな Http client で単純 なことをやるならこれでも十分 • Flutter China コミュニティ が開発(主に一人) • メンテが若干不安 • 機能が豊富で Cancel, Cache や Cookie の制御や retrofit like なジェネレータもある • で人気がある https://github.com/flutterchina/dio https://github.com/dart-lang/http

Slide 88

Slide 88 text

dio + Cache https://github.com/flutterchina/dio # data_source.dart import 'package:dio/dio.dart'; @override Future getNews() async { return _dio.get>>( '/v1/wasabeef', queryParameters: { 'apiKey': 'VEhJUyBJUyBBUElLRVk', }, options: buildCacheOptions(const Duration(hours: 1)), ) .then((response) =>= News.fromJson(response.data)); } # pubspec.yaml dependencies: dio: ^3.x.x dio_http_cache: ^0.2.x 何かしら他の分野でも Http client を使ったこと ある人であれば理解は難し くないと思います。

Slide 89

Slide 89 text

Serializable/Deserialize

Slide 90

Slide 90 text

Serializable/Deserialize View Repository ViewModel Local Source Remote Data Source DI 特にサーバ API 通信で使 われる送受信したデータを シリアライズとデシリアラ イズ。

Slide 91

Slide 91 text

主な Serializable/Deserialize のパッケージ  json_serializable package   Freezed package  • 公式に Google が開発 • アノテーションを元に自動生成 • to/from JSON • 有名な方が個人開発 • json_serializable のサポートし ている • アノテーションを元に自動生成 • toString, hashCode.. • copyWith • Immutable https://github.com/rrousselGit/freezed https://github.com/google/json_serializable.dart

Slide 92

Slide 92 text

Freezed part 'article.freezed.dart'; part 'article.g.dart'; @freezed abstract class Article with _$Article { factory Article({ @required Source source, String author, @required String title, @required String description, @required String url, String urlToImage, @required DateTime publishedAt, String content, }) = _Article; factory Article.fromJson(Map json) =>= _$ArticleFromJson(json); } この例では、@freezed アノ テーションをつけて、 _$Article (自動生成されるク ラス)の mixin を適用していま す。 Freezed は json_serializable にも対応しているので、 _$ArticleFromJson のイン ターフェースも生成されていま す。 https://github.com/rrousselGit/freezed

Slide 93

Slide 93 text

Assets/Fonts

Slide 94

Slide 94 text

Assets/Fonts View Repository ViewModel Local Source Remote Data Source DI アプリ内で画像やフォ ントなどのファイルを 使いたい場合の設定を 説明していきます。

Slide 95

Slide 95 text

Assets - ローカル画像ファイルへのアクセス # pubspec.yaml flutter: assets: - assets/images/ - assets/images/icons/ pubspec.yaml に画像ファイルを置いたところのパスを assets: に指定します。 ディレクトリを指定した場合は、そのディレクトリ内は全て精査されますが、サ ブディレクトリ内までを再帰的には精査しません。

Slide 96

Slide 96 text

Assets - ローカル画像ファイルへのアクセス デバイスのピクセル密度毎によって 2.0x, 3.0x のディレクトリに配置します。 ※pubspec.yaml に指定する必要はありません。

Slide 97

Slide 97 text

Android (AAPT) の R.java に該当するもの が Flutter の世界では公式にはなく、画像な どのリソースへのアクセスはファイルパスを 文字列で ”assets/images/profile.jpg” で指定 しないといけません。 Assets - ローカル画像ファイルへのアクセス

Slide 98

Slide 98 text

Assets - ローカル画像ファイルへのアクセス /// main.dart Widget build(BuildContext context) { return Image.asset(‘assets/images/icons/profile.jpg'); } 例えば、Image class にローカルの画像を指定したい場合には以下のようにパス を指定します。

Slide 99

Slide 99 text

Assets - ローカル画像ファイルへのアクセス /// main.dart Widget build(BuildContext context) { return Image.asset(‘assets/images/icons/profile.jpg'); } ただ、文字列で指定するのはとてもタイプセーフだと言えず R.java って良くで きた仕組みだなぁって感じることとなります。。

Slide 100

Slide 100 text

Fonts - ローカルフォントへのアクセス # pubspec.yaml flutter: fonts: - family: RobotoMono fonts: - asset: assets/fonts/RobotoMono-Regular.ttf - asset: assets/fonts/RobotoMono-Bold.ttf weight: 700 好きな Fonts を使いたい場合にも pubspec.yaml に指定が必要となります

Slide 101

Slide 101 text

Fonts - ローカルフォントへのアクセス /// main.dart Widget build(BuildContext context) { return Text( 'Hi there', style: TextStyle( fontFamily: 'RobotoMono', ), } フォントもファミリー名を文字列で指定します。

Slide 102

Slide 102 text

もしタイポしていた場合 拡張子が間違っていたなどのタイポ(❌ jpeg、⭕ jpg)でも ランタイムエラーになるので、気をつけないといけません。 The following assertion was thrown resolving an image codec: Unable to load asset: assets/images/profile.jpeg /// main.dart Widget build(BuildContext context) { return Image.asset(‘assets/images/icons/profile.jpeg'); }

Slide 103

Slide 103 text

R.java, R.swift, SwiftGen, FlutterGen プロモーションを含みます

Slide 104

Slide 104 text

FlutterGen /// main.dart Widget build(BuildContext context) { return Image.asset(‘assets/images/icons/profile.jpg'); } FlutterGen を使えば、コマンド一つでタイプセーフにクラスを自動生成します プロモーションを含みます /// main.dart Widget build(BuildContext context) { return Assets.images.profile.image(); }

Slide 105

Slide 105 text

FlutterGen /// main.dart TextStyle(fontFamily: FontFamily.robotoMono), フォントも自動生成されます /// main.dart TextStyle(fontFamily: 'RobotoMono') プロモーションを含みます

Slide 106

Slide 106 text

FlutterGen プロモーションを含みます FlutterGen https://github.com/FlutterGen/flutter_gen さらに詳しく知りたい場合は

Slide 107

Slide 107 text

Test

Slide 108

Slide 108 text

Mockito /// Real class class Dog { String sound() =>= "Woof"; } /// Mock class class MockCat extends Mock implements Cat {} test('Dog Sound Test', () async { var dog = MockDog(); dog.sound(); verify(dog.sound()); when(dog.sound()).thenReturn("Grrrr"); expect(dog.sound(), "Grrrr"); }); https://github.com/dart-lang/mockito Android アプリ開発 でもよく使われるモッ ク化ライブラリの Mockito の Dart 版 が Google 公式とし て公開されているの で、最初はこのパッ ケージを導入してみる ことをお勧めします。

Slide 109

Slide 109 text

Lint/Analyze

Slide 110

Slide 110 text

複数人でプロダクション開発していく上では 特に、コードの可読性や保守性を保つために はコード規約に則って維持していくことが当 たり前の時代になりました Lint/Analyze

Slide 111

Slide 111 text

Effective Dart style # main.dart library peg_parser.source_scanner; import 'file_system.dart'; import 'slider_menu.dart'; # main.dart library pegparser.SourceScanner; import 'file-system.dart'; import 'SliderMenu.dart'; 例:Lower snake case でディレクトリやファイル名をつけること。

Slide 112

Slide 112 text

Linter, Analyzer 設定 プロジェクトルートに analysis_options.yaml を作成することでルールの設定が可能になります # analysis_options.yaml analyzer: exclude: - "**/*.g.dart" strong-mode: implicit-casts: false implicit-dynamic: false ..... linter: rules: - camel_case_types .....

Slide 113

Slide 113 text

 effective_dart style  Effective Dart に準拠 した Lint ルール Linter, Analyzer 設定  pedantic style  Google 内部で採用され ている Lint ルール  flutter style  Flutter analyze コマン ドで適用されている Lint ルール https://dart-lang.github.io/linter/lints/index.html

Slide 114

Slide 114 text

Flutter Architecture Blueprints プロモーションを含みます https://github.com/wasabeef/flutter-architecture-blueprints これらのサンプルコードを見たい方は

Slide 115

Slide 115 text

References * Docs https://flutter.dev/ * Samples https://github.com/brianegan/flutter_architecture_samples * Images https://unsplash.com/ https://www.irasutoya.com/

Slide 116

Slide 116 text

@wasabeef_jp wasabeef