Flutter Add To ANDPAD App

53b0cf6dfd1077b8159c9f34607938e1?s=47 くどう
October 22, 2020

Flutter Add To ANDPAD App

53b0cf6dfd1077b8159c9f34607938e1?s=128

くどう

October 22, 2020
Tweet

Transcript

  1. Flutter Add To ANDPAD App Copyright © 2020 Present ANDPAD

    Inc. This information is confidential and was prepared by ANDPAD Inc. for the use of our client. It is not to be relied on by and 3rd party. Proprietary & Confidential 無断転載・無断複製の禁止 potatotips #71
  2. 自己紹介 - kotaro.kudo - 株式会社アンドパッド アプリ開発チーム - GitHub: ham-burger -

    Twitter: derakudo - SIerで金融システム開発→別の会社でtoCアプリ開発→現職 - Flutterはここ3ヶ月くらい
  3. はじめに - 9月のアップデートでFlutterのAdd To Appを導入しました - あまり導入事例を見かけないので共有

  4. アジェンダ 1. Add To App 2. モチベーション 3. なぜReact Nativeじゃないのか

    4. 実装方法 5. 良かったこと 6. 悩んだこと 7. 残課題 8. ドキュメントをちゃんと読んでおけば・・・ 9. 今後の展望
  5. Add To App - ネイティブアプリにFlutterモジュールを取り込む仕組み - Flutter側でモジュールを作成し、ネイティブとFlutterはMethodChannelを利用して メッセージを送受信する

  6. モチベーション - リリース済みアプリが5つ。今後も増える見込み Kotlin/Swift Kotlin/Swift Flutter Swift ReactNative →Flutter(WIP)

  7. モチベーション - アプリの数とやりたいことに対して開発リソースが足りない - アプリ開発を担当している正社員は 4人 - それぞれの社員がAndroidとiOS両方の対応を行っている - 複数回同様の実装をしているときの虚無感

    - 少しでも開発スピードを上げたい
  8. なぜReactNativeじゃないのか - ReactNativeにも同様の仕組みはあるらしい - https://reactnative.dev/docs/integration-with-existing-apps - アプリチーム内の雰囲気やその他諸々を考慮 - https://tech.andpad.co.jp/entry/2020/02/12/114953 -

    https://techplay.jp/event/784745
  9. 実装方法 - https://flutter.dev/docs/development/add-to-app/android/project-setup - https://github.com/flutter/samples/tree/master/add_to_app

  10. 良かったこと - 開発スピードが上がった - hot reloadがあるのでビルド待ちが少ない - モジュール単体で動作確認できるように debug用画面を用意しておく -

    特にiOSネイティブは開発中のビルド待ち時間が長いイメージ - 実装差分を気にするコストは無くなった
  11. 悩んだところ - モジュール化の単位(Viewだけ?画面ごと?) - アプリ共通の処理は既にネイティブ側で実装済みなのでそれを活用するか迷った - 今回の画面でいうとhttpなど - 今回は認証トークンなど最低限のデータのみ連携して呼び出せるようにした -

    テストもFlutterにまとめたかった V VM M V Android Flutter V M V M VM ネイティブ側でアレコレせずに ... 最低限のデータ 全部のデータ Flutter側で完結するようにする 似たような 共通クラス
  12. 残っている課題 - Flutterの状態管理は必要 - Add To App自体はFlutter Moduleを呼び出すだけで、画面遷移をマネジメントしてくれるわけでは ない -

    呼び出し直後は透過、 MethodChannelで画面遷移させた後に fadeで表示させている - ベストプラクティスがまだわからない A B C A A C Android Flutter Androidの画面遷移に追従して いないので、そのままだと前回 呼び出した画面のまま
  13. 残っている課題 - クリーンビルドが遅くなった - CIがなんか遅いとの苦情が - FlutterとAndroidどちらもビルドするから - 未解決。神待ち -

    アプリサイズが大きくなった - AndroidではAPKで40MBほど - AABだともう少し小さい?(未確認) - toCだとDynamic feature moduleなど使ってインストールコストを下げる工夫が必要かもしれない
  14. ドキュメントをちゃんと読んでおけば・・・ - Flutterモジュールを触れない人も環境を整える必要があった - ソースコードビルドする方法を選択していた - 配布形式をAARにすることで改善 - https://flutter.dev/docs/development/add-to-app/android/project-setup#option-a---depend -on-the-android-archive-aar

  15. 今後の展望 - アプリのモジュール化推進の1手段として検討していきたい - https://tech.andpad.co.jp/entry/2020/07/30/191711

  16. ご清聴ありがとうございました 株式会社アンドパッドはアプリエンジニアを大募集中です! 様々な技術に挑戦できる環境があります、奮ってご応募ください! 採用ページ : https://recruit.andpad.co.jp/ Tech Blog : https://tech.andpad.co.jp

    HR ブログ : https://note.com/andpad_hr
  17. 実装のサンプル(Android)

  18. 対象画面 - ページングする一覧画面 - タップするとWebViewの詳細画面を開く - WebViewはネイティブ側の既存実装を活用 - 開発中の仕様変更が少なそうだった -

    最初は簡単なところから試すほうが良さそう
  19. 実装方法 - リポジトリの準備 kudo_kotaro@kudo-kotaronoMacBook-Pro add-to-app-sample % ls MyApplication my_flutter

  20. 実装方法 - setting.gradle include ':app' // assumed existing content rootProject.name

    = "My Application" setBinding(new Binding([gradle: this])) // new evaluate(new File( // new settingsDir.parentFile, // new 'my_flutter/.android/include_flutter.groovy' // new ))
  21. 実装方法 - gradle dependencies { implementation project(':flutter') // new }

  22. 実装方法 - Flutter engineの有効化 class AndpadApplication : MultiDexApplication() { lateinit

    var flutterEngine: FlutterEngine override fun onCreate() { super.onCreate() flutterEngine = FlutterEngine(this) flutterEngine.dartExecutor.executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) FlutterEngineCache .getInstance() .put("flutterEngine", flutterEngine) } }
  23. 実装方法 - ActivityでFlutter画面起動 - FlutterはFragmentとして起動したほうが扱いやすい気がする class SampleActivity : AppCompatActivity() {

    private val flutterEngine by lazy { (applicationContext as AndpadApplication).flutterEngine } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val fragment: FlutterFragment = FlutterFragment .withCachedEngine("flutterEngine") .build() supportFragmentManager .beginTransaction() .add(R.id.container, fragment) .commitNow() } }
  24. 実装方法 - MethodChannel経由でルーティングと必要な情報の連携 private val methodChannel by lazy { MethodChannel(flutterEngine.dartExecutor,

    "methodChannel") } val arguments = mutableMapOf( // 起動画面判別用のパス "path" to "/sample_paging_list", ).apply { // 共通パラメータ(認証トークンetc) putAll(getMappedFlutterHttpClientParams()) } // Fragment起動後にFlutterに画面遷移を通知 methodChannel.invokeMethod("navigate", arguments)
  25. 実装方法 - Flutter側のルーティング処理 methodChannel = MethodChannel("methodChannel"); methodChannel.setMethodCallHandler((call) async { switch

    (call.method) { case "navigate": // 画面遷移の処理 } });
  26. 実装方法 - Flutter側のルーティング処理 case "navigate": final arguments = call.arguments; String

    path = arguments["path"].toString(); switch (path) { case "sample_paging_list": // 画面Object生成処理 default: return null; }
  27. 実装方法 - 画面にデータを連携する処理 case "sample_paging_list": final accessToken = arguments["access_token"] as

    String; return Navigator.of(context) .push(MaterialPageRoute(builder: (context) { final itemId = arguments["item_id"] as int; return SamplePagingListScreen( accessToken: accessToken, itemId: itemId); }));