【Flutter】そのディレクトリ構成は恋される
そのディレクトリ構成は恋される
タイトルのとおりですが、スマホアプリエンジニアの私が個人的に恋している(推している)Flutterのディレクトリ構成を紹介しようと思います。
私の思想としてはクラスの役割とUTのしやすさに設計しております。
Flutterでアプリ開発をする際は基本的にこのディレクトリ構成を使用しております。
ディレクトリ構成は一長一短ありますので、気に入らない方はスルーしてください。
前提
- Dart:
2.17.1
- Flutter:
3.0.2
- アーキテクチャ:
MVVM
- 状態管理:
riverpod
MVVM
についての説明は割愛しますので、詳細が知りたい方は以下をご参照ください。
ディレクトリ構成
ディレクトリ構成を紹介します。
.
├── android
├── ios
├── assets
│ ├── images
│ └── json
├── coverage
├── lib
│ ├── component
│ ├── constant
│ ├── core
│ ├── infrastructure
│ ├── model
│ ├── provider
│ ├── repository
│ ├── router
│ ├── ui_core
│ ├── view
│ ├── view_model
│ ├── app.dart
│ ├── importer.dart
│ └── main.dart
└── test
├── constant
├── core
├── model
├── provider
├── repository
├── ui_core
└── view_model
各ディレクトリの説明を行います。
android
これはFlutterプロジェクトを生成したときに自動で作られるフォルダです。
Androidに関する設定やソースが格納されています。
ios
これはFlutterプロジェクトを生成したときに自動で作られるフォルダです。
iosに関する設定やソースが格納されています。
assets
アプリに関するアセット周りのデータを格納します。
画像やアプリに必要な情報が記載されているjsonファイルなどを格納することが多いです。
coverage
UTによるカバレッジに関するデータを格納するフォルダです。
coverageフォルダは以下のindex.htmlを閲覧すればそのアプリのUTのカバレッジを見れるようにしています。
lib
プロダクトコードを管理するフォルダです。
私は基本的にはフォルダ名を複数型にしません。
フォルダの中に複数のファイルが格納されるのは当たり前だからです。
component
アプリで使用する共通で再利用するUIを管理します。
Reactでよく言うAtomicDesign
のmolecule
やatom
まで細かく切っていません。
個人的には粒度で得られるメリットがあまり感じられないため、componentフォルダに再利用するUIを格納しています。
constant
アプリで使用する定数の管理をします。
Androidで言うResource
のような役割を持っています。
constantフォルダによく格納するファイルは以下の通りです。
-
colors.dart
- アプリで使用する色の定義をします。独自のマテリアルカラーも定義します。
-
config.dart
- アプリで使用する設定ファイルです。ロジックを変えずとも設定ファイルの値を書きれば動きが変わるような値を定義しています。
-
dimens.dart
- アプリで共通で使用するUIに関する定義をします。
- margin
- padding
- size
- duration
- fontSize
- etc...
- アプリで共通で使用するUIに関する定義をします。
-
strings.dart
- アプリで使用する文言を定義します。
- 基本的に文字列のハードコードはしない思想です。
-
urls.dart
- アプリで使用するURLを定義します。
core
アプリのコアとなるクラスを管理します。
例えば、FirebaseのTimestamp型とDatetime型を変換するコンバーターやアプリ全体で使用するLoggerなどを管理することが多いです。
infrastructure
API通信やデータベース管理などを担当します。
APIなど実際に通信を行うクラスです。
Repositoryクラスのコンストラクタに渡して使用しています。
model
アプリの型定義のクラスを管理します。
イミュータブルな思想のため、freezedを使用しています。
freezed
で自動生成されたファイルも格納されています。
あと、model配下にuiフォルダを切って、UIの状態を表すuiステートのモデルを格納しています。
Viewはこのuiステートを監視して、表示を切り替えるような設計思想です。
provider
ViewModelやRepositoryを使用するにあたり、それらを提供するProviderを管理します。
状態管理は基本的にriverpodを使用しています。
repository
ビジネスロジックの役割を持つクラスを管理します。
infrastructureに定義したAPIクライアントでAPIなどでデータをCRUDする関数を呼び出します。
Repositoryで定義した関数はViewModelから呼び出すようにしています。
基本的にはXxxxxRepostiory
というabstract class
を作り、別途それを実装したクラスXxxxxRepostioryImpl
を作ることが多いです。
上記はUTのしやすさを考慮しているためです。
router
アプリの画面遷移を担当するクラスを管理します。
このクラスはViewからのみ呼び出すようにしています。
Viewは自身のContextを渡すだけで遷移したい画面に遷移でき、遷移処理は全てこのクラスに移譲しています。
ui_core
UIに関するコアとなる機能を持つクラスを管理します。
例としては以下を管理することが多いです。
- 日付型と日付文字列の変換処理(表示文字列の生成)
- 入力フォームのトリム処理
- 入力フォームのバリデーター
view
アプリの画面となるクラスを管理します。
ホーム画面や設定画面や登録画面などが挙げられます。
ViewはComponentのクラスを参照してUIを構築します。
基本的にUIのボタンイベントなどの操作は、ViewModelで定義したメソッドを呼び出します。
view_model
アプリのUIステートを保持し、画面に表示するデータを変換する役割を持つクラスを管理します。
Repositoryで定義した関数を呼び出し、データを加工し、自身が保持しているuiステートを更新するようにします。
uiステートを更新されればViewが持っているbuild関数が呼ばれて自動的に表示が変わるイメージです。
ViewModelに定義している関数はViewから呼ばれるようにしています。
main.dart
アプリの一番上位(エントリーポイント)となるファイルです。
以下のようなことを行うことが多いです。
- 画面を縦に固定する
- Firebaseの初期化処理
- envファイルの読み込み
- riverpodのスコープ設定
app.dart
main.dart
でrunApp
に渡す上位のアプリケーションクラスです。
以下のようなことを行うことが多いです。
- flutter_screenutilによるレスポンシブ設定
- アプリ全体のテーマの設定
importer.dart
上記で記載したファイルを全部エクスポートするファイルです。
各クラスはimporterを経由してクラスを参照するようにしています。
test
UTコードを管理するフォルダです。
基本的にはlib
フォルダで作られているフォルダを切るようにしています。
プロダクトコードのUTコードを一意に特定するためです。
まとめ
私が個人的に恋している(推している)Flutterのディレクトリ構成を紹介しようと思います。
基本はAndoridベースになっているように思えます。
FlutterではBloc
やFlex
などのアーキテクチャがありますが、自分は使ったことはないので一度使ってみようと思います。
また、その時の良し悪しなどを共有できればいいなと考えています。
Discussion