Flutter プロジェクト立ち上げる時に必要な手順
現在の Flutter の最新
3.7.9-stable
テンプレートなどがない場合の、Flutter プロジェクト立ち上げの手順
pubspec.yaml
pubspec.yaml
name: hoge_app
description: hoge
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=2.19.5 <3.0.0'
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
# 状態管理
flutter_riverpod: ^2.3.2
# immutable
equatable: ^2.0.5
freezed_annotation: ^2.2.0
json_annotation: ^4.8.0
# UI
cupertino_icons: ^1.0.2
adaptive_dialog: ^1.8.2
auto_size_text: ^3.0.0
flutter_svg: ^2.0.4
extended_image:
# Device
permission_handler:
image_picker:
share_plus: ^6.3.1
package_info_plus: ^3.0.3
# Dart 言語の拡張
intl: ^0.17.0
collection: ^1.17.0
# ロガー
simple_logger: ^1.9.0+2
dev_dependencies:
flutter_test:
sdk: flutter
# 静的解析
flutter_lints: ^2.0.0
# Freezed
freezed: ^2.3.2
json_serializable: ^6.6.1
# Code generator
build_runner: ^2.3.3
flutter_gen_runner: ^5.2.0
# icon
flutter_launcher_icons: ^0.12.0
# native splash
flutter_native_splash: ^2.2.19
flutter_gen:
output: lib/gen/
colors:
inputs:
- assets/color/colors.xml
flutter:
generate: true
uses-material-design: true
assets:
- assets/images/
flutter_icons:
android: true
ios: true
1. flutter create の実行
flutter create hoge_app
# 必要そうなオプション
## 必須プラットフォームのみの生成(ios, android のみでしか使わないのに、
## web とかのネイティブ実装混じってるのよくあるので、やってほしい)
flutter create hoge_app --platforms [ios, android, windows, linux, macos, web]
## 組織id
## これがないと、iOS の bundleId とかが .example になって後から直すのめんどくさい
flutter create hoge_app --org hogeOrg
やること
- 必要なオプションをつけて、flutter create の実行
2. fvm の導入
asdf もありますが、なんかたまにバグるので個人的には fvm 一択です。
優しい人は、.tool-version
も作ってあげましょう。
前までは README とかに Flutter のバージョンを書いたりしてましたが、更新忘れたりするとめんどくさいので、fvm_config.json
参照してもらったほうがいいです(README 見た後に、結局 fvm_config.json に確認しにいったりするので)。
やること
- fvm use の実行
.gitignore
に.fvm/flutter_sdk
の追加
3. ディレクトリの分割
DDD …的な、普段使ってる or プロジェクトメンバーと相談などして決めたディレクトリを作成し、.gitkeep ファイルを作っておきます。
ディレクトリ構造の例
自分が普段使ってるのは以下の構造です。
多くは、hukusuke1007/flutter_app_template を参考にしています。
lib
├── domain/
│ ├── entities/
│ ├── repositories/
│ └── use_cases/
│
├── presentation
│ ├── pages/
│ │ ├── home/
│ │ ├── settings/
│ │ ...
│ │
│ ├── res/
│ │ ├── color_shemes.dart
│ │ ├── theme.dart
│ │ └── typography.dart
│ │
│ └── widgets/
│
├── utils/
│ ├── extensions/
│ │ ├── build_context_ex.dart
│ │ ...
│ │
│ ├── logger.dart
│ ├── flavor.dart
│ ...
│
├── app.dart // <= MaterialApp を置く
└── main.dart // <= runApp() を置く
やること
- ディレクトリ構造の確定。
- ディレクトリのそれぞれの使い方を共有するドキュメントを README などに残す。
- まだファイルの入っていないディレクトリに .gitkeep を入れておく。
4. dart-define-from-file で環境変数を設定する
同時に、アイコンの設定もやっていきます。
↓ 村松さんの神記事があるので、これとほぼ同じことをやっていきます。
この記事をもとに変更した内容は、記事へのリンクを貼るだけでなく、コメントをつけるか、PR などに手順を全て残して置くことをお勧めします。
やること
- 記事の通りの手順を実装して、dart-define-file の作成。
- flavor.dart を置いておく。
5. 静的解析ファイルの編集
flutter_lints はすでに入っているので、中身を書いていきます。
デフォルトの設定でも運用可能ですが、デフォルトにはないルールなど、ちゃんと設定しましょう。
特に、追加したほうがいいものを書いておきます。
# これがないと、dynamic を返す関数が容認されてしまう
always_declare_return_types: true
# 個人的には相対パスがお勧めです。
prefer_relative_imports: true
or
always_use_package_imports: true
# どっちでもいいが、" と ' がどっちも許されているのはよくない
prefer_single_quotes: true: true or false
# 余計な変更差分を増やさないために、true がお勧めだが好み
require_trailing_commas: true or false
# 議論が分かれるところなので、どちらにしよ明示的にしておくのが理想です
use_build_context_synchronously: true or false
6. ロガーの実装
print
では警告が出て(設定次第だが)、ロガーにはさまざまな便利なパッケージがあるので、導入しておくのがお勧め。
debugPrint
でもいいが、問題が出たファイルに飛べるようになったり、ログレベルを設定したりできるので、パッケージとかを使うのがお勧めです。
おすすめ紹介など色々あるので、色々試してみてください。
重要な機能だが、ただのログ出力なので、悩みすぎて時間取られないように注意
- https://zenn.dev/flutteruniv_dev/articles/20220413-153500-flutter-logger
- https://medium.com/flutter-jp/logger-ec25d8dd179a
グローバルlogger
変数などで、どこからでも使えるようにするのがお勧めです。
↓ 例
やること
- ロガーを実装、グローバル変数で提供する。
7. flutter_gen の導入
画像やフォントなどのアセットを、直接文字列で指定することを防げます。
以下、flutter_gen の引用。
❌ Bad
What would happen if you made a typo?Widget build(BuildContext context) { return Image.asset('assets/images/profile.jpeg'); } // The following assertion was thrown resolving an image codec: // Unable to load asset: assets/images/profile.jpeg
⭕️ Good
We want to use it safely.Widget build(BuildContext context) { return Assets.images.profile.image(); }
color 指定を xml ファイルでやる方法もお勧めですが、どっちでもいいです。
やること
- flutter_gen の導入
8. テーマの実装
MaterialApp.theme
, MaterialApp.darkTheme
に渡す ThemeData
です。
ライトテーマとダークテーマ、とりあえず両方作っておくことをお勧めします。
ライトテーマのみの実装の場合は、MaterialApp.darkTheme
にライトテーマと同じ変数を渡すなどするのが良いと思います。
実装のポイントは、色以外の部分の実装を共通化することです。
ダークテーマとライトテーマで設定が重複しないように工夫しましょう。
デザインがマテリアルデザインに準拠している場合は、ColorScheme
クラスで場合分けをするのがお勧めです。
import '../../gen/colors.gen.dart';
class AppColorScheme {
const AppColorScheme._();
static ColorScheme get light => const ColorScheme.light().copyWith(
// primary
primary: ColorName.primary,
onPrimary: ColorName.white,
// surface
surface: ColorName.white2,
onSurface: ColorName.black,
onSurfaceVariant: ColorName.lightGray,
// background
background: ColorName.white,
//...
);
static ColorScheme get dark => const ColorScheme.dark()
}
import 'color_schemes.dart';
class AppTheme {
static ThemeData _baseThemeData({
required ColorScheme colorScheme,
required TextTheme textTheme,
}) {
final baseData = ThemeData.from(
colorScheme: colorScheme,
textTheme: textTheme,
);
return baseData.copyWith(
// scaffold
scaffoldBackgroundColor: colorScheme.background,
appBarTheme: const AppBarTheme(
centerTitle: true,
),
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: ColorName.darkGray,
selectedItemColor: colorScheme.onSurface,
unselectedItemColor: colorScheme.onSurfaceVariant,
),
// button
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
elevation: 0,
),
),
// input
inputDecorationTheme: const InputDecorationTheme(
// ...
),
// ...
);
}
static ThemeData get light {
return _baseThemeData(
colorScheme: AppColorScheme.dark,
textTheme: Typography.material2021(),
);
}
static ThemeData get dark {
return light;
}
}
return MaterialApp(
theme: AppTheme.light,
darkTheme: AppTheme.dark,
);
やること
- テーマデータの実装
9. 多言語対応
これは任意です。
画面に表示する文字列を変数に入れて、対応する言語ごとに切り替えるという技術です。
安定なのは Flutter 標準の flutter_localizations です。
そのほか、色々な多言語対応パッケージを比較しているものがあるので、試してみてください。
やること
- 多言語対応
10. Navigation のルール設定
Flutter におけるページ遷移は色々なので、ルールを決めておくことを推奨します。
名前付きルーティングのパッケージもあるので、採用して、ルールを決めておきましょう。
僕はしばらくは go_router とかは使わないので、Flutter 標準の Navigator 1.0 でのルール例を書いときます(Navigator 2.0 はキャッチアップできてない)。
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
/// 4. Equatable で引数の設定 => `toString` , `operator ==` が override されてるなど便利
class HomePageArgs extends Equatable {
const HomePageArgs({required this.title});
final String title;
List<Object?> get props => [title];
}
class HomePage extends StatelessWidget {
const HomePage._(this.args);
final HomePageArgs args;
/// 1. ルートの名前文字列
static const routeName = '/home';
/// 2. Route の static メソッドの実装
static Route<void> route(HomePageArgs args) {
return MaterialPageRoute(
/// 3. RouteObserver のログのため、RouteSettings は必須
settings: RouteSettings(name: routeName, arguments: args),
builder: (_) => HomePage._(args),
);
}
Widget build(BuildContext context) {
return const Scaffold(
body: Center(child: Text('HomePage')),
);
}
}
// 使う側
onPressed: () {
Navigator.of(context).push(HomePage.route());
}
ボトムナビゲーションバーのページ遷移に関しては、以下のような実装をお勧めします。
やること
- ナビゲーション方法の確定
- ナビゲーションシステムの実装
11. そのほか、使いそうなパッケージのインポート
ガチでプロジェクトによるので、例だけ貼っときます。
使わないパッケージは抜いておくなどの対応は常にしておきましょう。
パッケージのリストは、分類分けしておくことを推奨します。
やること
- パッケージのインポート
12. 共通コンポーネントの作成
確認ダイアログ、エラーダイアログ、スナックバーなど、実装者どうしで競合しそうなものを、すでに作っておきましょう。
プロジェクトがスムーズになります。
やること
- 共通コンポーネントの実装
.vscode/settings.json
の設定
13. スニペットなども作っておくと便利です。
{
"cSpell.words": [
"Bitcode",
"cupertino",
"LTRB",
"Rgba",
"riverpod",
"unawaited",
"unfocus",
"xcconfig",
],
"cSpell.ignorePaths": ["vscode-extension", "node_modules", ".git", ".vscode", "**/settings.json", ".firebaserc"],
"[dart]": {
"editor.formatOnSave": true,
"editor.formatOnType": true,
"editor.rulers": [80],
"editor.selectionHighlight": false,
"editor.suggest.snippetsPreventQuickSuggestions": false,
"editor.suggestSelection": "first",
"editor.tabCompletion": "onlySnippets",
"editor.wordBasedSuggestions": false,
"editor.defaultFormatter": "Dart-Code.dart-code",
"editor.codeActionsOnSave": {
"source.organizeImports": true,
"source.fixAll": true
}
},
"dart.renameFilesWithClasses": "always",
// flutter のパスを fvm にする
"dart.flutterSdkPath": ".fvm/flutter_sdk",
"dart.flutterGenerateLocalizationsOnSave": "all",
// Remove .fvm files from search
"search.exclude": {
"**/.fvm": true
},
// Remove from file watching
"files.watcherExclude": {
"**/.fvm": true
}
}
Discussion