🐈
Flutterアプリのプロジェクト構成を解説
はじめに
プロジェクトのディレクトリ構成はコードの可読性や保守性に大きく影響します。
特にプロジェクトが成長するにつれて、整理された構成は開発効率を高め、バグの発見や修正を容易にする鍵となります。
適切なディレクトリ構成を採用することで開発チーム全体が一貫したコーディングスタイルを維持し、コードの品質を保つことができます。
本記事では、Flutterプロジェクトにおけるディレクトリ構成の例をご紹介します。
また、今回ご紹介するプロジェクト構成は前提としているアーキテクチャがあります。
別の記事で解説しているので、未読の方はそちらを参照してください。
プロジェクト構成
プロジェクト構成
[project_root]/
├── assets/
├── env/
├── lib/
│ ├── main.dart
│ └── src/
│ ├── root.dart
│ ├── api/
│ │ ├── api_impl.dart
│ │ ├── interface/
│ │ │ └── api.dart
│ │ └── response/
│ │ └── [xxx]_response.dart
│ ├── app_state/
│ │ └── [xxx]_state.dart
│ ├── framework/
│ │ ├── di/
│ │ │ ├── interface/
│ │ │ │ └── service_locator.dart
│ │ │ └── service_locator_impl.dart
│ │ ├── error/
│ │ │ ├── errors.dart
│ │ │ └── result.dart
│ │ ├── module/
│ │ │ ├── interface/
│ │ │ │ └── [xxx].dart
│ │ │ └── [xxx]_impl.dart
│ │ └── util/
│ ├── model/
│ │ └── [xxx].dart
│ ├── repository/
│ │ ├── interface/
│ │ │ └── [xxx]_repository.dart
│ │ └── [xxx]_repository_impl.dart
│ ├── router/
│ │ ├── route_path.dart
│ │ ├── router_config.dart
│ │ └── routes.dart
│ ├── screen/
│ │ └── [xxx]/
│ │ ├── [xxx]_screen.dart
│ │ └── [xxx]_view_model.dart
│ ├── service/
│ │ ├── interface/
│ │ │ └── [xxx]_service.dart
│ │ └── [xxx]_service_impl.dart
│ ├── theme/
│ │ └── theme.dart
│ ├── view_controller/
│ │ ├── [xxx]_impl.dart
│ │ └── interface/
│ │ ├── [xxx].dart
│ │ └── [xxx].dart
│ └── widget/
│ ├── container/
│ │ └── [xxx]
│ │ └── [xxx].dart
│ └── presentation/
│ └── [xxx]
│ └── [xxx].dart
└── test/
├── e2e/
└── unit/
flutter create
コマンドで生成されるディレクトリで配置を変えないものは省略しています。
- レイヤーファーストの構造でアーキテクチャの各コンポーネントを中心に配置
- 階層が深くなりすぎないように配慮
assets/
- 画像やフォントなどのリソースファイルを配置
- 公式ドキュメントを参考にしてプロジェクトルートに配置
env/
- ビルド時に渡す定数や環境変数を管理
- 環境ごとにファイルを分ける
-
--dart-define-from-file
に渡すので.json
と.env
の好きな方でいい
lib/main.dart
- アプリのエントリーポイントになるソースファイル
- 一番最初に参照されるので
lib
の直下に配置
lib/src/
- dartにおいて外部に公開されないコードを配置
-
main.dart
とそれ以外の関係を明確にするためにsrc
を挟む
lib/src/root.dart
-
runApp
に渡すウィジェットツリーのルートになるウィジェット - 他のウィジェットとの関係を明確にするために
lib/src
の直下に配置
lib/src/api/
- API通信に関するコードを配置
- APIのモックを想定して
interface
を切る - レスポンスの定義はコード量が増えがちなので、
response
に分ける
lib/src/app_state/
- アプリ全体で共有される状態を管理
- riverpodの
keepAlive: true
で常に保持される
lib/src/framework/
- 各レイヤーで共通して利用するコードを配置
-
framework/
直下は適切な名前でディレクトリを切る
lib/src/model/
- アプリで扱うドメインのデータモデルを配置
- モデルにはビジネスロジックを持たせない
lib/src/repository/
- データの永続化や外部APIとの通信を行うコードを配置
lib/src/router/
- ルーティングに関するコードを配置
lib/src/screen/
- 画面ごとのウィジェットとViewModelを配置
-
screen/
直下は画面ごとにディレクトリを切る - 画面ごとにディレクトリには
[xxx]_screen.dart
と[xxx]_view_model.dart
以外にも専用のウィジェットのソースを配置してもいい
lib/src/service/
- アプリケーションで利用するビジネスロジックを配置
lib/src/theme/
- アプリケーション全体で利用するテーマやUIスタイルを定義
lib/src/view_controller/
- ViewModelで利用するUIロジックを分離するためのコードを配置
lib/src/widget/
- 各画面で利用する共通ウィジェットを配置
-
presentation
とcontainer
に分ける-
presentation
は表示するデータをパラメータとして受け取り、表示のみを行うウィジェット -
container
は表示するデータを取得するためのロジックを持ち、取得したデータをpresentation
ウィジェットに渡す -
presentation
とcontainer
の中はそれぞれUIの種類ごとにディレクトリを切る
-
test/
- テストコードを配置
- ユニットテストとE2Eテストを分けて配置
ローカルパッケージを利用する場合
アプリをプロジェクトルートに配置したまま
[project_root]/
├── lib/
└── packages/
└── [other_package]/
└── lib/
- アプリのプロジェクトルートに
packages
ディレクトリを作成して、その配下に各ローカルパッケージを配置 - ciやタスクランナー等でディレクトリパスが変わると困る場合に有効
モノレポ構成
[project_root]/
└── packages/
├── [app]/
│ └── lib/
└── [other_package]/
└── lib/
- 各パッケージを
packages
ディレクトリに配置 - モノレポ用のツールが使える
まとめ
Flutterプロジェクトにおけるディレクトリ構成の例をご紹介しました。
アーキテクチャを明確にし、それに合わせた構成を採用することで開発効率を向上させ、コードの品質を保つことができます。
また、プロジェクトの成長に合わせてディレクトリ構成を見直すことも重要です。
開発メンバーの入れ替わりやアーキテクチャに影響のある変更を行う際に、プロジェクト構成を見直すとより効果的に変更を適用できるでしょう。
最後に、この記事で紹介したプロジェクト構成が少しでも参考になれば幸いです。
Discussion