Slide 1

Slide 1 text

Flutter Flutter の活用事例 (株式会社サイバーエージェント)

Slide 2

Slide 2 text

はじめに 新規アプリを Flutter で作り始める際に考えたこと (株式会社サイバーエージェントの場合) Image by jcomp on Freepik

Slide 3

Slide 3 text

● エディターは? ● CI は? ● 動作確認・テストは? ● どういう技術使いたい? ● 開発期間は? ● Flutter 経験者は? ● どの技術を使えばシステム要件を満 たすことが出来る? 要件定義 エンジニアの体制 モチベーション ● アーキテクチャどうする? ● 参考にできるものはある? ● 慣れてる MVVM?Redux? Flutter の将来性 開発体験 アーキテクチャ はじめに 01 02 03 04 05 06 ● Flutter で大丈夫?

Slide 4

Slide 4 text

システム要件や機能要件などから Flutter でも最後まで作り切れるかどうかを判断す る。 要件定義 要件定義 Image by freepik on Freepik

Slide 5

Slide 5 text

5 対象端末 ● Android と iOS が対象? ● タブレット最適化はある? ● テレビデバイスは? 要件定義 Image by macrovector on Freepik

Slide 6

Slide 6 text

● 動画再生(ライブ、オンデマンド) ● 課金 ● PUSH 通知 ● 検索 ● OS 毎に違うデザイン 機能要件は? 要件定義 WINTICKET

Slide 7

Slide 7 text

Flutter では難しいことのポイント ● ライブラリなどで対応していない機能を iOS/Android をそれぞれで作る事になった時 ● Swift/Kotlin で作るほどのパフォーマンス Flutter では難しいこと 要件定義 ● ユーザー認証(Firebase など) ● 単純な動画再生(ライブ、オンデマンド) ● 課金 ● PUSH 通知 ● 検索 ● OS で共通したデザイン Flutter でも出来ること Flutter だと難しいこと ● OS 毎に全く違うデザイン ○ Flutter を使うメリットがない ● リッチなアニメーションでパフォーマンス ● OS 最新機能への追従

Slide 8

Slide 8 text

ここで説明する体制は一つの例にしか過 ぎませんが、一般的に Swift/Kotlin でアプ リ開発する場合にはリリーススケジュール を合わせることを考えると同人数程度必要 になってくると思います。 エンジニアの体制 エンジニアの体制 サーバエンジニア Web エンジニア iOS エンジニア Android エンジニア

Slide 9

Slide 9 text

少ない人数でも開発を始められる クロスプラットフォームでアプリが開発でき るのであれば結果として少ない人数で iOS/Android アプリが作れることとなる。 または、iOS/Android それぞれ一人だった 場合には Flutter にすることでコードレ ビューを出来て結果として品質向上に繋っ たりもします。 エンジニアの体制 エンジニアの体制 サーバエンジニア Web エンジニア Flutter エンジニア

Slide 10

Slide 10 text

ここで説明するのは現在携わっているプロ ジェクトの状況です。 Android/iOS 経験者であれば Flutter 開 発は敷居が低いのかなと思っていますし、 アプリ開発経験がなくても Web の React 経験者があれば Flutter の開発も敷居が 思ったほど高くないと思っています。 エンジニアの体制 エンジニアの体制 要件定義 & 技術検証 アーキテクチャ & 環境構築 Flutter 経験者 1 人 Android 経験者 1 人 Flutter 経験者 1 人 Android 経験者 2 人 機能開発 Flutter 経験者 1 人 Android 経験者 2 人 iOS 経験者 1 人 新卒 1 人

Slide 11

Slide 11 text

モチベーション モチベーション 結局のところ、その技術を使いたいかどう かが技術選定にも大きく関わってくると思う のでやりたいならやってみましょう。 Image by jigsawstocker on Freepik

Slide 12

Slide 12 text

6 つのプラットフォーム (Android、iOS、Web、 Windows、macOS、Linux) での Flutter サポートによ り、100 万以上ものアプリが Flutter を利用していま す。 サイバーエージェントの場合は新規にアプリを作るもの 関してはほぼ Flutter を選択しています。 Flutter の将来性 Flutter の将来性 Image by iwat1929 on Freepik

Slide 13

Slide 13 text

Flutter の開発体験はすごい良いと思いま す。ホットリロードも問題無く動きますし、エ ディターのプラグインなども豊富です。 何より開発中は Flutter Web をフル活用 することでチーム開発でのやりやすさが向 上します。 開発体験 開発体験 Image by Gaelle Marcel on Unsplash

Slide 14

Slide 14 text

● Device Preview ● Widgetbook Flutter Web を活用する 開発体験 Image by flaticon on Freepik

Slide 15

Slide 15 text

Device Preview Device Preview は別のデバイスでの表示をシミュ レートしてくれるパッケージです。 例えば Flutter Web (Chrome) 上で iPhone で表 示した時の UI を出してくれます。 開発体験 Flutter Gallery https://pub.dev/packages/device_preview

Slide 16

Slide 16 text

Flutter Web (Chrome) で iPhone や Samsung 端末をシミュレートしたり端 末の向きを変えています。 Flutter Web にさえすれば GitHub で コードレビューする際などで動作確認 が楽になります。 Device Preview 開発体験 https://pub.dev/packages/device_preview

Slide 17

Slide 17 text

例えば、GitHub の PR に対して IssueOps として GitHub Actions で Device Preview をビルド して GitHub Pages には PR 毎にデプロイし、コメントにそ の URL を貼り付けるようにしています。 その後、マージされたらこの URL は削除しています。 GitHub の PR で動作させる 開発体験

Slide 18

Slide 18 text

また、main branch にマージされた際にも main branch の状態を GitHub Pages に デプロイされる ようにし、README に URL を貼って確認できるように しています。 ※ GitHub Pages は一般公開しないように Private 設定にしています。 GitHub の PR で動作させる 開発体験

Slide 19

Slide 19 text

Widgetbook フロントエンド界隈で利用されている Storybook の Flutter 版です。UI コンポーネ ントをカタログのように表示することができ、 Flutter Web にも対応しているので GitHub Pages などにデプロイすることで Web 上で 表示できます。 ※ Screenshot の取得には対応していない ので別でやる必要があります。 開発体験 Maestro https://www.widgetbook.io/

Slide 20

Slide 20 text

● Playbook + REG SUIT ● Maestro Testing 開発体験 Image by Kristina Paparo on Unsplash

Slide 21

Slide 21 text

Playbook + REG SUIT Playbook は UI コンポーネントのスクリーンショット を撮るためのパッケージ。 REG SUIT は Visual Regression Test (VRT) のため のツール。 Playbook で取得したスクリーンショットを REG SUIT で読み込み VRT を行っています。 https://github.com/playbook-ui/playbook-flutter https://github.com/reg-viz/reg-suit 開発体験

Slide 22

Slide 22 text

Maestro E2E テストが出来るフレームワークです。 YAML でテストケースを書き iOS/Androidエ ミュレータで実行することができます。 YAML で書ける点と YAML 内で JavaScript を実行することができるので他のツールに比 べると柔軟性が高いです。 開発体験 Maestro https://maestro.mobile.dev/

Slide 23

Slide 23 text

アプリのアーキテクチャを考える上で一番重要に考え ていることは状態管理をどう扱うかだと思っています。 ※ 基本的にアーキテクチャは、世の中的にはもっとい いと言われるものがあったとしてもチームとして合意が 取れている状態であれば正解です。 アーキテクチャ アーキテクチャ Image by liravega on Freepik

Slide 24

Slide 24 text

グローバルステート 基本的にはサーバをグローバルス テートとして捉えているのでリクエス トデータのキャッシュがほとんど解 決してくれるはずです。 ただし、複数のスクリーンなどで使われ るような認証トークンなどには Riverpod で管理しています。 ローカルステート スクリーン、コンポーネント内で完 結するデータの管理方法です。よ くある例で UI に表示するローティ ングの状態だったり、ボタンの有 効無効の切り替え用だったりする ものは Flutter Hooks で管理して います。 アーキテクチャ アーキテクチャ サーバリクエストとキャッシュ GraphQL Flutter を利用しています。 GraphQL Flutter はレスポンスデータ のキャッシュもしてくれます。

Slide 25

Slide 25 text

GraphQL Flutter サーバリクエストとキャッシュは GraphQL Flutter が担ってもらっています。サーバリクエ ストに必要な情報(Firebase Auth の Id Token など)以外は基本的にグローバルス テートとしては持たないようにしています。 https://pub.dev/packages/graphql_flutter https://pub.dev/packages/graphql_codegen 開発体験 サーバリクエストとキャッ シュ graphql_flutter graphql_codegen

Slide 26

Slide 26 text

サーバリクエストと キャッシュ GraphQL Flutter GraphQL の設計については Fragment Colocation を基本設計としており、それぞれ のコンポーネントで必要なデータは Fragment としてそれぞれでコンポーネントと 同様の場所で .graphql ファイルを定義して います。 Query は画面から呼びます。 アーキテクチャ Maestro Home Screen User Component Feed Component query Home { user { ...UserParts } feed { ...FeedParts } } # ユーザー情報 fragment UserParts on User { id } mutation CreateAccount { signUp { user { id } } } # フィード情報 fragment FeedParts on Feed { id title body } https://pub.dev/packages/graphql_flutter https://pub.dev/packages/graphql_codegen

Slide 27

Slide 27 text

GraphQL Flutter 右のディレクトリ構成のようにコンポーネント と同階層のところに .graphql の定義を置い ています。 サーバリクエストと キャッシュ アーキテクチャ Maestro lib/ ├── data │ └── schema │ └── main.graphql └── ui └── screen ├── home │ ├── component │ │ ├── item │ │ │ ├── feed_item.dart │ │ │ └── feed_item_fragment.graphql │ │ └── text │ │ ├── user_name.dart │ │ └── user_name_fragment.graphql │ ├── home_query.dart │ └── home_screen.dart ├── mypage └── settings https://pub.dev/packages/graphql_flutter https://pub.dev/packages/graphql_codegen

Slide 28

Slide 28 text

ローカルステート アーキテクチャ Maestro @override Widget build(BuildContext context) { final isLoading = useState(false); ./ ... 何か return Scaffold( body: Stack( children: [ const Text('Body'), if (isLoading.value) const Center( child: CircularProgressIndicator(), ), ], ), ); } https://maestro.mobile.dev/ Flutter Hooks の useState(...) Flutter Hooks 例のようにこのウィジェット内で完結するよう なデータの場合は Flutter Hooks の useState(...) を利用して管理しています。 それ以外にも useContext や useCallback などもよく利用しています。

Slide 29

Slide 29 text

グローバルステート Riverpod 基本的にはサーバをグローバルステート として捉えているのでリクエストデータの キャッシュがほとんど解決してくれるはず です。 ただし、複数のスクリーンなどで使われるよう な認証トークンなどには Riverpod で管理し ています。 アーキテクチャ Maestro https://maestro.mobile.dev/ part 'id_token_state.g.dart'; typedef IdToken = String; @Riverpod(keepAlive: true, dependencies: [firebaseAuth]) class IdTokenState extends _$IdTokenState { @override IdToken build() { return ''; } ./ 初期値は空 Future fetch() async { final user = ref.watch(firebaseAuthProvider).currentUser; if (user .= null) return; update(await user.getIdToken()); } void update(String token) { if (state .= token) state = token; } } Riverpod の Notifier @override Widget build(BuildContext context) { final idToken = ref.watch(idTokenStateProvider); ./ ... 何か }

Slide 30

Slide 30 text

全体のディレクトリ構造はこういう形で定義し ています。作るにつれて苦しくなっていく箇所 は随時考え直しています。 ディレクトリ構成 アーキテクチャ Maestro lib/ ├── data/ │ ├── network/ │ └── system/ ├── foundation/ │ ├── extension/ │ └── firebase/ │ ├── auth/ # Firebase Auth 関連 │ └── messaging/ # Firebase Messaging 関連 ├── state/ # グローバルの状態管理関連 ├── ui/ # UI 関連のモジュール(GraphQL は各画面で定義) │ ├── screen/ │ │ └── home/ │ │ ├── home_screen.dart # 画面 │ │ ├── home_screen_e2e.yaml # Maestro のファイル │ │ ├── home_screen_vrt.dart # Visual Regression Test のファイル │ │ ├── hook/ # 画面固有の Hooks │ │ │ └── use_update_home.dart │ │ ├── component/ # 画面固有のコンポーネント │ │ │ ├── home_navigation.dart │ │ │ ├── home_card.dart │ │ │ └── home_card_fragment.dart # コンポーネントの GraphQL ファイル │ │ └── top │ │ ├── home_top_screen_e2e.yaml │ │ └── home_top_screen.dart │ ├── theme/ # グローバルテーマ設定 │ │ ├── app_theme.dart │ │ └── app_text.dart │ ├── hook/ # 汎用的な Hooks │ │ ├── use_debounce.dart │ │ ├── use_debounce_test.dart │ │ ├── use_sign_in.dart │ │ ├── use_sign_in_test.dart │ │ ├── use_sign_out.dart │ │ └── use_sign_out_test.dart │ └── component/ # 汎用 UI コンポーネント │ ├── fab/ │ └── text/ └── use_case/

Slide 31

Slide 31 text

Thank You Daichi Furiya / wasabeef Google Developers Expert/CyberAgent, Inc