ぶらりとスタンプラリー
ログイン画面 | ホーム画面 | 参加中画面 |
---|---|---|
開発マシンの OS は Mac、IDE は VSCode の利用を前提としています。
次の記事を参考にして、fvm
をインストールして、fvm
コマンドが使える状態にしましょう。
FVMでFlutter SDKのバージョンをプロジェクト毎に管理する
次のコマンドを実行して本プロジェクト指定の Flutter SDK をインスト−ルをしましょう。
fvm install
本アプリで使用する Google マップ用の API キーを取得して、カレントディレクトリで次のコマンドを実行してください。このコマンドを実行しないとアプリは起動しません。
# 引数で与えられた環境変数を基にビルドに必要な `lib/util/env/env.dart` を作成してくれます。
# 作成された `lib/util/env/env.dart` を直接編集しても大丈夫です。
bin/flutter_env -g [Google マップ用の API キー]
パラメータ | 説明 | |
---|---|---|
-g |
Must |
Google マップ用の API キーの値 |
-h |
ヘルプを表示します。 |
次の記事を参考にして、Git コミット時に Prefix や絵文字を簡単に追加できるツールである commitizen と git-cz をインスト−ルしましょう。
Commitizenを使ってgitのコミットメッセージをしっかり書こう
適当なファイルを修正・ステージングにあげて git cz
というコマンドを打って次のような表示が出れば OK です。
開発環境のセットアップが終わったら、VSCode 右下から実行するデバイスを選択し、実行したい Configuration を選択して実行してみましょう。
Configuration 名 | プラットフォーム | 接続先の Firebase |
---|---|---|
app-dev | iOS / Android | flutteruniv-stamp-rally-dev |
app-prod | iOS / Android | flutteruniv-stamp-rally-prod |
go_router_builder
や freezed
を使った自動生成ファイルを変更した場合は次のコマンドを実行してください。
make build-runner
ポイントは Cloud Functions は裏で動くこと。
- flutter_riverpod v2 + go_router
- CODE WITH ANDREA と DDD のアーキテクチャを参考にして、本アプリは下記の4層アーキテクチャで実装しています。
ユーザーとの I/F を担う層。Application 層と Domain 層に依存する。Infrastructure 層に依存してはいけない。
ページや UI 部品の Widget クラス群。State を監視( watch / listen )して UI に表現する。ユーザーイベントを検知して Service のメソッドを呼び出す。キャッシュが効かなくなるので直接 Repository Interface を呼び出してはいけない。画面遷移の実装は Widget のイベントハンドラー内で行ってよい。
アプリケーションのロジックや状態を定義する層。Domain 層に依存する。Presentation 層と Infrastructure 層に依存してはいけない。
アプリのあらゆる状態。Domain 層の Entity そのものでもよいし、複数の Repository をまたいだ Entity を統合するクラスでもよい。State は StateProvider
等でラップされ Widget や他の State から参照される。
ユーザーイベントを受け付けて、複数の Repository Interface を呼び出して Entity を受け取って State を更新するサービスクラス。Widget からのメソッド呼び出しや、依存する State の更新を契機に発火する。
ドメインロジックやドメインオブジェクト(エンティティ)を定義する層。どの層にも依存してはいけない。
Repository Interface で扱うドメインオブジェクト(データの構造体)。入力値のバリデーションは Entity のコンストラクタで実装する。Infrastructure 層が投げる例外はドメイン層で定義する。
データの永続化や外部サービス連携を担う Repository のインターフェース。集約毎に定義する。
データの永続化や外部サービス連携を担う層。Domain 層に依存する。Presentation 層と Application 層に依存してはいけない。
Repository Interface の実装。Data Source を利用してデータの永続化を行う。
様々なデータソース。Firebase だったり、API だったり、Hive だったり、SharedPreferences だったり、Isar だったりする。
├── application アプリケーション層
│ └── <関心事>
│ ├── <state> 状態クラス群
│ └── <関心事>_service.dart サービスクラス
├── domain ドメイン層
│ ├── exceptions.dart 例外クラス
│ └── repository
│ └── <集約>
│ ├── <集約>_repository.dart リポジトリのインターフェースクラス
│ └── entity 集約単位のエンティティ
├── infrastructure インフラストラクチャ層
│ └── <データソース>
│ └── <集約>
│ └── <集約>_repository.dart リポジトリの実装
├── presentation
│ ├── app.dart アプリケーション
│ ├── router.dart ルーティング
│ ├── theme.dart テーマ
│ ├── component プレゼンテーション層で共通の Widget
│ └── page
│ └── <関心事>
│ ├── component 画面単位の Widget
│ └── <関心事>_<CURD>page.dart 画面Widget
└── util どの層からもアクセス可能な便利クラス(ロガー、拡張メソッドなど)
ユーザー操作を契機とした処理の流れの例を図にしました。Riverpod の ref.listen()
を使ってローディング表示や SnackBar の表示を制御しています。図には示していませんがエラー時のエラーダイアログの表示も制御できます。
手続き的に処理をするよりも、コードが簡素になりますしコードの再利用もしやすくなりますが、少し理解しづらいので図にしてみました。
基本的に 関心事 毎にファイルを分割しています。1つのファイルに複数のクラスがあっても問題ありません。ファイル名は「ファイル名 = クラス名」とはせず 関心事.dart とします。
git-flow を採用しています。
- 参考サイト
上のブランチモデルに従います。具体的には次のとおりです。
develop
ブランチに移動(checkout
)して最新の状態にする(pull
)- 例
feature/0-hoge
ブランチを作成して移動する
- ブランチ名の
0
は Issue 番号、hoge
は実装する内容を端的に表した言葉です - すべてハイフン区切りでお願いします
- Feature ブランチ上で実装をする
- 実装が完了したら GitHub サイト上で
develop
ブランチ向けにプルリクエストを作成する
- 原則レビュー不要、必要に応じて誰かにレビュー依頼を出して下さい
- GitHub サイト上でマージを実行
- Issue を Close する
発生した時点で stamp-rally-firebase に Issue を起票して修正をしてください。
GitHub Actions を利用して CI を構築しています。
発火タイミング | 実行内容 |
---|---|
developにマージ PR作成・更新 |
コードフォーマット 静的解析(flutter analyze) テスト(flutter test) Androidリリースビルド 結果を共同開発用のSlackに通知 |
対応していません。