ネイティブエンジニアの桐山です。
Timersでは新規事業として、毎月無料でましかくプリントを印刷できるサービスを始めました! 新規アプリでFlutterを採用し、3ヶ月でiOS・Androidアプリをリリースした話の概要編をお届けします。
はじめに
この度弊社で新しい家族向けアプリをFlutterで作りました!
apps.apple.com
おおまかなフロー
Why Flutter?
弊社では元々家族アルバムアプリFammや年賀状アプリをネイティブで開発しており、写真を扱うネイティブ開発の知見も人的リソースもありました。新規アプリの開発するにあたって、ネイティブで開発した方が良いのでは?という意見が多かったのですが、Fammプリントは早くMVPでリリースしたいという要求があったため、Flutterが候補に上がってきました。
実際、年明けに1週間がっつりFlutterを触ってみると、ネイティブとさほど遜色ないUX、開発工数が短縮できるという展望が見えたので、Flutterを採用することに決めました。
また、今後新規アプリをスピーディーに両OSのアプリを機能をそろえた状態で検証を行うための、技術的な引き出しとして持っておきたいという会社目線での目的もありました。
余談ですが、弊社では月1で全社員がリモートで集まって各事業の様子や目標の進捗などを共有する場があるのですが、Flutterで新規アプリを作ると決まった後にLTでFlutterの紹介を行いました。
開発体制
全員Flutterでのアプリ開発経験はありませんでした。
※ 詳細についてはこちら note.com
技術選定
紆余曲折はありながら最終的に以下のような構成になりました。1つ1つ解説していきます!
アーキテクチャ
- MVVM + UseCase
- BlueprintアプリなどもMVVMを採用しており、ベンチマークとなるサンプルがあった方が開発も進めやすいことから、採用しました。
- Androidアプリの名残から、ViewModelからロジックを追い出したかったのでUseCaseも場所によって採用しています。
状態管理、画面遷移、依存性注入
おそらくFlutter開発の最初の肝となる部分になるかと思いますが、FammプリントではGetXを採用しました。
諸々については別の記事で解説をする予定です。
そのほか使用したライブラリ
Alice
https://pub.dev/packages/alice
面倒な設定なしに、通信ログを表示してくれます。
Cached network image
https://pub.dev/packages/cached_network_image
Imageクラスにエクステンションをして共通コンポーネント化しています。
extension ImageExtensions on Image { static CachedNetworkImage cachedNetwork(String url) => CachedNetworkImage( imageUrl: url, useOldImageOnUrlChange: true, cacheKey: url, errorWidget: (context, url, error) { debugPrint(error.toString()); return const Center( child: Icon( Icons.image_not_supported_rounded, color: AppColors.secondaryText, ), ); }, ); }
dio
GetXでも通信クラスは用意されているのですが、謎のエラーに遭遇して解決できなかったかつ先ほど紹介したAliceを組み合わせられなかったことから、通信においてはdioというライブラリを使用しています。
freezed
https://pub.dev/packages/freezed
toJson
/fromJson
/toString
などのボイラープレートをコマンドひとつで自動生成してくれます。
また、UIの状態を表現するデータオブジェクトを作るのにもとても便利です。
@freezed class UploadUiState with _$UploadUiState { const factory UploadUiState.idle() = Idle; const factory UploadUiState.success( List<OrderImage> orderImages, ) = UploadSuccess; const factory UploadUiState.processing() = Processing; const factory UploadUiState.uploading( int totalCount, int progress, ) = Uploading; const factory UploadUiState.complete() = Complete; }
lottie
https://pub.dev/packages/lottie
弊社では初めてイラストアニメーションを取り入れたUIを提供しました。 このライブラリではLottieのアニメーション定義ファイルを1行で描画してくれます。
Lottie.asset('assets/lottie/hoge.json')
ネイティブコード、OS差分について
ネイティブコードは今のところ0%
- iOSの
Info.plist
・Build PhaseやAndroidのbuild.gradle
は触るので最低限そこだけ理解できればiOS/Android開発未経験でも十分開発を進めることが可能な印象です。 - 画像処理でうまくいかない事象があり、その部分をネイティブコードで書くことで解決できないかを検討中です。
OS差分、OS判定を入れている箇所
- 通信時のUserAgent
- 分析に必要なデバイス名、バージョン情報の取得
- Push通知許諾、写真アクセス許諾、IDFA許諾アラートをiOSのみ表示
- ボタンの角丸
- アラート、ダイアログ、ナビゲーションバー、ボトムナビゲーション、ローディングをOSごとに出しわけ
- アイコンの出しわけ
- Push通知受信設定
UIについてはできるだけそれぞれのOSで自然に見えるようにOS判定を入れて表示の出しわけをしていますが、割り切ってOS共通で同様のUIにすることでOS判定は上記からかなり減らせると思います。
Test
静的解析
- 標準のflutter lintsを使用しています。
Unitテスト
- ViewModelでロジックがある部分は基本的にUnitテストを書く方針にしています。
- Mockオブジェクトの生成にはMockitoを使用しています。
UIテスト
- Flutter公式の呼び方はIntegration Testとなっています。
- 新規会員登録〜注文完了までのフローでUIテストを書くことに挑戦したのですが、OSが表示するUI(アラート、写真ピッカーなど)を操作できないため一旦保留になりました。
- github.com
- issueにも上がっているので状況はウォッチしていきたいです。
開発スピード向上のための事前準備
UIコンポーネントの定義
FammのDesign Systemに則り、あらかじめ使用するであろうコンポーネントを定義することでスムーズな開発ができたと思います。
- 色、テーマ
- フォント
- スペース
- ボタン
- テキスト
- テキストフィールド(テキスト入力・選択)
- アラート、ダイアログ、ナビゲーションバー、ボトムナビゲーション、ローディングをOSごとに出しわけするラッパークラス
CI(Bitrise)
テストアプリを配信するワークフロー
静的解析結果はDangerがFlutter対応していなかったため、GitHubのPRにコメントする運用
flutter analyze --write=flutter_analyze_report.txt
を実行して結果をtxtに保存- envmanを使用して環境変数に結果を保存
#!/usr/bin/env bash RESULT=$(<$BITRISE_SOURCE_DIR/flutter_analyze_report.txt); if [ -z "$RESULT" ]; then RESULT="No issue."; // 空文字だとGitHubコメント時にエラーになる fi envman add --key FLUTTER_ANALYZE_RESULT --value "$RESULT"
- GitHubのPRにコメント
開発を進める中で工夫したこと
開発に必要な情報をまとめたドキュメント
弊社では知見や設計、仕様、手順書などをesaにまとめる文化があるのですが、今回のFlutter開発においても1つのesaに情報を集約することであれなんだっけ?の時間を減らすことができたと思います。
目次
コードレビュー
iOS・Androidアプリでは基本的に1人のApproveがあればマージできる運用にしていますが、下記の理由により最低Approveを2人にする運用に変更しました。
- 知識の横展開を広く行いたかった
- 全員がFlutter初心者のため目を増やしたかった
モブ会
毎週火曜木曜で45分定期モブ会をセッティングしています。アジェンダがない会はスキップする運用ですが、リリースから1ヶ月経った今もスキップ率はかなり低めなので価値は高いと感じています。
話していること例
- スプリント中に実装している内容で詰まっている箇所の相談
- 設計の相談
- 実装した内容で今後使えそうなものの共有
- Flutter最新情報の共有
- YouTubeの動画鑑賞
- Flutter Widget of the Weekは新しい気づきもあり有意義でした
Flutter Widget of the Week を見て取り入れたものの一部
最後に
実際にFlutterでアプリ開発を進めていくうちに、以前のiOS開発にはもう戻れなくなってしまいました(後日この辺りの話も記事にできればと思います)。
Flutterを採用するメリット・デメリットはそれぞれ多々あるかと思いますが、メンバーやプロダクトの特徴によって適切に技術選定することをおすすめします。
今後のFlutterの発展が楽しみです!
PR
子育て家族アプリFammを運営するTimers inc.では現在エンジニアを積極採用中! オンラインでの面談やカジュアルランチなどもやってますので是非お気軽にご連絡ください!