💃

【Flutter】そのディレクトリ構成は恋される

2022/06/26に公開

そのディレクトリ構成は恋される

タイトルのとおりですが、スマホアプリエンジニアの私が個人的に恋している(推している)Flutterのディレクトリ構成を紹介しようと思います。
私の思想としてはクラスの役割UTのしやすさに設計しております。
Flutterでアプリ開発をする際は基本的にこのディレクトリ構成を使用しております。
ディレクトリ構成は一長一短ありますので、気に入らない方はスルーしてください。

前提

  • Dart: 2.17.1
  • Flutter: 3.0.2
  • アーキテクチャ: MVVM
  • 状態管理: riverpod

MVVMについての説明は割愛しますので、詳細が知りたい方は以下をご参照ください。
https://qiita.com/sdkei/items/a48ae24536562ed000b3

ディレクトリ構成

ディレクトリ構成を紹介します。

.
├── 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でよく言うAtomicDesignmoleculeatomまで細かく切っていません。
個人的には粒度で得られるメリットがあまり感じられないため、componentフォルダに再利用するUIを格納しています。

constant

アプリで使用する定数の管理をします。
Androidで言うResourceのような役割を持っています。
constantフォルダによく格納するファイルは以下の通りです。

  • colors.dart
    • アプリで使用する色の定義をします。独自のマテリアルカラーも定義します。
  • config.dart
    • アプリで使用する設定ファイルです。ロジックを変えずとも設定ファイルの値を書きれば動きが変わるような値を定義しています。
  • dimens.dart
    • アプリで共通で使用するUIに関する定義をします。
      • margin
      • padding
      • size
      • duration
      • fontSize
      • etc...
  • 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.dartrunAppに渡す上位のアプリケーションクラスです。
以下のようなことを行うことが多いです。

  • flutter_screenutilによるレスポンシブ設定
  • アプリ全体のテーマの設定

importer.dart

上記で記載したファイルを全部エクスポートするファイルです。
各クラスはimporterを経由してクラスを参照するようにしています。

test

UTコードを管理するフォルダです。
基本的にはlibフォルダで作られているフォルダを切るようにしています。
プロダクトコードのUTコードを一意に特定するためです。

まとめ

私が個人的に恋している(推している)Flutterのディレクトリ構成を紹介しようと思います。
基本はAndoridベースになっているように思えます。
FlutterではBlocFlexなどのアーキテクチャがありますが、自分は使ったことはないので一度使ってみようと思います。
また、その時の良し悪しなどを共有できればいいなと考えています。

Discussion