😻

Flutterでよく使われるアーキテクチャやコアライブラリの選択肢

に公開

開発の規模に応じて、使用するアーキテクチャやライブラリは変わります。今まで遭遇した様々なライブラリの中で、複数の選択肢があるものをメモしてます。

「ここ間違ってない?」や「こういうのもあるよ!」などあれば教えて下さい! 😆

アーキテクチャ

個人的には MVVM がいいと思います。シンプルでカスタマイズしやすいためです。
より厳格にレイヤー毎に分割したい場合は、パッケージ分割するのも一つの手だと思います。

MVVM (Model-View-ViewModel)

前まではwasabeefさんの flutter-architecture-blueprint を使用していました。

https://github.com/wasabeef/flutter-architecture-blueprints#app-architecture

View(見た目)と ViewModel(ビジネスロジック)と Model(外部 API をリポジトリパターンで抽象化したレイヤ)に分けるシンプルな設計です。
Riverpod、Freezed、Retrofit など安定しているパッケージを採用してます。

しかし、2022 年 6 月にアーカイブされました。
パッケージが更新されておらずそのまま使用はできないのですが、アーキテクチャとしては現在でも使用できると思います。

また、Flutter 公式がこちらの MVVM パターンをベストプラクティスとして挙げているため、こちらのアーキテクチャにするのが一番安全かと思います。

https://docs.flutter.dev/app-architecture/guide

しかしこちらのコードは外部ライブラリを使用せずに実装されているため、本番環境で使用するには、Riverpod、Freezed など導入する必要があります。

マルチパッケージアーキテクチャ

Kosuke Saigusa(@kosukesaigusa)さんが Omiai というアプリを開発するときに採用したアーキテクチャみたいです。

https://zenn.dev/omiai_techblog/articles/omiai-flutter-architecture?redirected=1

MVVM のように UI、ビジネスロジック、データ層をレイヤー毎に分けています。
しかし、それぞれをパッケージ化することで、それぞれ依存パッケージを設定することができ、ビジネスロジックに BuildContext が混ざることを避けられるのがメリットです。

DDD (Domain-Driven Design)

Eric Evans によって提唱されたアーキテクチャです。ドメインという物事の関心とそれ以外を分けることが特徴です。DDD では主に以下の 4 つに分けられ、必ず上から下に依存しなければなりません。

  • User Interface
  • Application
  • Domain
  • Infrastructure

https://www.infoq.com/jp/minibooks/domain-driven-design-quickly/

Clean Architecture

よく見るクリーンアーキテクチャの構成図に基づいています。DDD をより細かく分けたものです。実務での使用経験はないため、詳細は分かりません。

https://zenn.dev/koudai/articles/8ee15d10008f55

View のステート管理とライフサイクル

flutter_hooks はまだメジャーリリースされていないので、StatefulWidget を使用するのが良さそうです。

StatefulWidget

StatefulWidget は State を持ちます。State 内のインスタンス変数は StatefulWidget が破棄されるまで値を保持します。setState()を呼び出すと、Flutter は build メソッドを再度呼び出し、画面を再描画します。
また、ライフサイクルでは initState や dispose などのフェーズごとに処理を記述します。

https://docs.flutter.dev/ui/interactivity

flutter_hooks

https://pub.dev/packages/flutter_hooks

flutter_hooks はステート管理とライフサイクルを React の hooks 風に書けるようにするライブラリです。Widget は StatefulWidget ではなくHookWidgetを継承します。
ステート管理にはuseStateを使用し、ライフサイクルは各コントローラーごとに記述されます。

ライフサイクルは、それぞれのコントローラーごとに記述されます。Flutter で標準で入っているコントローラーなどはすでに実装されていてそのまま使うことができます。
例えば、useAnimationControllerの initState や dispose の処理はここに書かれています。
また、毎回忘れずに書かないといけない初期化・破棄処理を隠蔽することができると言うメリットもあります。

ViewModel の実装

ViewModel にはステート管理と変更を View に通知する機能が必要です。
Riverpod しか使用経験ありませが、BLoC パターンは勉強したい。

ChangeNotifier + Provider

https://docs.flutter.dev/data-and-backend/state-mgmt/simple

Flutter フレームワークに含まれている ChangeNotifier クラスと Provider パッケージを使用します。

Riverpod

https://pub.dev/packages/riverpod

Riverpod は Provider の後継で移行することが推奨されています。

https://riverpod.dev/ja/docs/from_provider/motivation

flutter_bloc

https://pub.dev/packages/flutter_bloc

flutter_bloc はイベントを stream で ViewModel に流し、更新後の state が ViewModel から View に流れる仕組みです。

参考: flutter_bloc の基本

画面遷移

公式では小さなアプリでは Navigator, ディープリンクや Web 対応する場合は Router が推奨されています。

https://docs.flutter.dev/ui/navigation

GetX

GetX は画面遷移以外にも多くの機能を提供しますが、多機能ゆえのバグに遭遇することもあります。アーキテクチャが崩壊しやすいため、使用は推奨しません。

https://api.flutter.dev/flutter/widgets/Navigator-class.html

基本的な画面遷移機能を提供します。

go_router + go_router_builder

https://pub.dev/packages/go_router

https://pub.dev/packages/go_router_builder

Navigator に比べて優れている点は 2 点です。

  1. ネストした画面遷移を簡単に作成できます。
  2. 画面に必要な引数の型を指定できるため、タイプセーフに使用できます。

端末内データ

sqflite

https://pub.dev/packages/sqflite

SQL を使用する場合に適しています。

shared_preferences

https://pub.dev/packages/shared_preferences

単純な key-value ストアです。

flutter_secure_storage

https://pub.dev/packages/flutter_secure_storage

セキュアなデータの保存に適しています。

多言語対応

業務だと何らかのサービスなりファイル形式に依存すると思うのでそれらにあったライブラリを使うと良さそうです。

個人開発の場合は1つの csv ファイルで管理し、一番感覚的に呼び出せる slang が良さそうです

パッケージ毎の機能比較はこちらを参考にさせてもらいました
https://zenn.dev/susatthi/articles/20220422-140216-flutter-localizations

flutter_localizations & intl

公式ドキュメントがあります

https://docs.flutter.dev/ui/accessibility-and-internationalization/internationalization

ARBという独自の翻訳ファイルのみ対応
ファイル分割に対応していないのでアプリが大きくなってくるとつらそうです
また、ビルダーが付いておらず context から呼び出すため、呼び出し方はAppLocalizations.of(context)!.titleで冗長になる

easy_localization

https://pub.dev/packages/easy_localization

対応ファイル形式が広い
呼び出し方は t.news.errorMessage.tr()のようになる。まだ冗長

slang

https://pub.dev/packages/slang

機能は easy_localization とほぼ同じ
呼び出し方は t.news.errorMessageのように一番直感的に書ける

リンター & フォーマッター ルールセット

個人的にはキツめのルールが好み
pedantic_mono と dart_code_metrics の recommended 設定がおすすめ

flutter_lints

https://pub.dev/packages/flutter_lints

flutter create するときに入ってるデフォルトの公式ルールセット
Dart 用の lints/recommended に Flutter 推奨ルールを追加したもの

pedantic_mono

https://pub.dev/packages/pedantic_mono

みんな大好き mono さんが作ったルールセット
デフォルトよりも強め

dart_code_metrics

https://dcm.dev/

有料のリンター、メトリクス計測ツール
無料分のリンタールールだけでもおすすめ

GitHubで編集を提案

Discussion