27 KiB
| description | applyTo |
|---|---|
| Dartコードの推奨記述ルールに従った、DartとFlutterコード作成のための指示事項。 | **/*.dart |
Dart と Flutter
DartとFlutterチームが推奨するベストプラクティス。これらの指示事項はEffective DartとArchitecture Recommendationsから取得されています。
Effective Dart
過去数年間で、私たちは多くのDartコードを書き、何が良く機能し、何がうまくいかないかについて多くを学びました。この知識を共有することで、あなたも一貫性があり、堅牢で、高速なコードを書けるようになります。2つの包括的なテーマがあります:
-
一貫性を保つ。 フォーマットや大文字小文字の使い分けなどについて、どちらが良いかの議論は主観的で解決不可能です。わかっていることは、一貫性が客観的に有用だということです。
2つのコードが異なって見える場合、それらは何らかの意味のある方法で異なっているべきです。コードの一部が目立って注意を引く場合、それは有用な理由でそうするべきです。
-
簡潔にする。 Dartは親しみやすいように設計されており、C、Java、JavaScript、その他の言語と同じ多くの文と式を継承しています。しかし、これらの言語が提供するものを改善する多くの余地があるため、Dartを作成しました。文字列内挿から初期化形式まで、さまざまな機能を追加して、より簡単で容易に意図を表現できるようにしました。
何かを言うための複数の方法がある場合、一般的に最も簡潔なものを選択すべきです。これは、プログラム全体を1行に詰め込むような
コードゴルフをすべきという意味ではありません。目標は経済的なコードであり、密集したコードではありません。
トピック
ガイドラインを理解しやすいように、いくつかの個別のトピックに分割しています:
-
スタイル – これは、コードのレイアウトと構成のルール、または少なくとも
dart formatがあなたのために処理しない部分を定義します。スタイルのトピックでは、識別子のフォーマット方法も指定します:camelCase、using_underscoresなど。 -
ドキュメント – これはコメント内の内容について知る必要があるすべてのことを説明します。docコメントと通常の日常的なコードコメントの両方について説明します。
-
使用法 – これは、動作を実装するために言語機能を最大限に活用する方法を教えます。文または式の中にあるものは、ここでカバーされます。
-
設計 – これは最もソフトなトピックですが、最も広い範囲を持ちます。ライブラリの一貫性があり使いやすいAPIを設計することについて学んだことをカバーします。型シグネチャや宣言にあるものは、ここで取り上げます。
トピックの読み方
各トピックはいくつかのセクションに分かれています。セクションにはガイドラインのリストが含まれています。各ガイドラインは以下のいずれかの語で始まります:
-
DO ガイドラインは、常に従うべき慣行を記述します。これらから逸脱する正当な理由はほとんどありません。
-
DON'T ガイドラインはその逆:ほとんど良いアイデアではないことです。願わくば、歴史的な負の遺産が少ないため、他の言語ほどこれらが多くないことです。
-
PREFER ガイドラインは、従うべき慣行です。しかし、そうでない方が理にかなっている状況もあるかもしれません。そうする場合は、ガイドラインを無視することの完全な意味を理解していることを確認してください。
-
AVOID ガイドラインは「prefer」の対偶:すべきでないことですが、稀に良い理由がある場合があります。
-
CONSIDER ガイドラインは、状況、先例、そして自分の好みに応じて、従うかどうかを決めることができる慣行です。
いくつかのガイドラインでは、ルールが適用されない例外を記述しています。リストされている場合、例外は網羅的でない可能性があります—他のケースでもあなたの判断を使う必要があるかもしれません。
これは、紐の結び方を間違えると警察があなたのドアを叩き壊すように聞こえます。そんなに悪いことではありません。ここにあるガイドラインのほとんどは常識的なことで、私たちは皆理性的な人間です。目標は、いつものように、読みやすく保守しやすい素晴らしいコードです。
ルール
スタイル
識別子
- 型には
UpperCamelCaseを使用する。 - 拡張には
UpperCamelCaseを使用する。 - パッケージ、ディレクトリ、ソースファイルには
lowercase_with_underscoresを使用する。 - インポートプレフィックスには
lowercase_with_underscoresを使用する。 - その他の識別子には
lowerCamelCaseを使用する。 - 定数名には
lowerCamelCaseを使用することを推奨する。 - 2文字より長い略語と省略形は単語のように大文字にする。
- 使用されないコールバックパラメーターにはワイルドカードを使用することを推奨する。
- プライベートでない識別子には先頭アンダースコアを使用しない。
- プレフィックス文字を使用しない。
- ライブラリに明示的に名前を付けない。
順序
dart:インポートを他のインポートより前に配置する。package:インポートを相対インポートより前に配置する。- すべてのインポートの後の別のセクションでエクスポートを指定する。
- セクションをアルファベット順にソートする。
フォーマット
dart formatを使用してコードをフォーマットする。- フォーマッターに優しくなるようにコードを変更することを検討する。
- 80文字以下の行を推奨する。
- すべてのフロー制御文に中括弧を使用する。
ドキュメント
コメント
- コメントは文のようにフォーマットする。
- ドキュメントにブロックコメントを使用しない。
Docコメント
- メンバーと型をドキュメント化するために
///docコメントを使用する。 - パブリックAPIのdocコメントを書くことを推奨する。
- ライブラリレベルのdocコメントを書くことを検討する。
- プライベートAPIのdocコメントを書くことを検討する。
- docコメントを単一文の要約で始める。
- docコメントの最初の文を独自の段落に分ける。
- 周囲のコンテキストとの冗長性を避ける。
- 主な目的が副作用である関数やメソッドのコメントは三人称動詞で始めることを推奨する。
- 非ブール変数またはプロパティのコメントは名詞句で始めることを推奨する。
- ブール変数またはプロパティのコメントは「Whether」に続けて名詞または動名詞句で始めることを推奨する。
- 値を返すことが主な目的の関数やメソッドには名詞句または非命令動詞句を推奨する。
- プロパティのgetterとsetterの両方にドキュメントを書かない。
- ライブラリまたは型のコメントは名詞句で始めることを推奨する。
- docコメントにコードサンプルを含めることを検討する。
- docコメントでスコープ内の識別子を参照するために角括弧を使用する。
- パラメーター、戻り値、例外を説明するために散文を使用する。
- docコメントをメタデータアノテーションの前に配置する。
Markdown
- Markdownを過度に使用することを避ける。
- フォーマットにHTMLを使用することを避ける。
- コードブロックにはバッククォートフェンスを推奨する。
記述
- 簡潔さを推奨する。
- 明らかでない限り、略語と頭字語を避ける。
- メンバーのインスタンスを参照するときは「the」の代わりに「this」を使用することを推奨する。
使用法
ライブラリ
part ofディレクティブで文字列を使用する。- 他のパッケージの
srcディレクトリ内にあるライブラリをインポートしない。 - インポートパスが
libに出入りすることを許可しない。 - 相対インポートパスを推奨する。
Null
- 変数を明示的に
nullに初期化しない。 nullの明示的なデフォルト値を使用しない。- 等価演算で
trueまたはfalseを使用しない。 - 初期化されているかどうかをチェックする必要がある場合は
late変数を避ける。 - null可能型を使用するために型昇格またはnullチェックパターンを検討する。
文字列
- 文字列リテラルを連結するために隣接する文字列を使用する。
- 文字列と値を組み合わせるために内挿を使用することを推奨する。
- 必要でない場合は内挿で中括弧を使用することを避ける。
コレクション
- 可能な場合はコレクションリテラルを使用する。
- コレクションが空かどうかを確認するために
.lengthを使用しない。 - 関数リテラルでの
Iterable.forEach()の使用を避ける。 - 結果の型を変更する意図がない限り
List.from()を使用しない。 - 型でコレクションをフィルタリングするために
whereType()を使用する。 - 近くの操作で済む場合は
cast()を使用しない。 cast()の使用を避ける。
関数
- 関数を名前にバインドするために関数宣言を使用する。
- ティアオフで済む場合はラムダを作成しない。
変数
- ローカル変数の
varとfinalに一貫したルールに従う。 - 計算できるものを保存することを避ける。
メンバー
- フィールドを不必要にgetterとsetterでラップしない。
- 読み取り専用プロパティにするために
finalフィールドを使用することを推奨する。 - 簡単なメンバーには
=>の使用を検討する。 - 名前付きコンストラクターにリダイレクトする場合やシャドウイングを避ける場合を除き、
this.を使用しない。 - 可能な場合は宣言時にフィールドを初期化する。
コンストラクター
- 可能な場合は初期化フォーマルを使用する。
- コンストラクター初期化子リストで済む場合は
lateを使用しない。 - 空のコンストラクター本体には
{}の代わりに;を使用する。 newを使用しない。- 冗長に
constを使用しない。
エラーハンドリング
on句なしのcatchを避ける。on句なしのcatchからのエラーを破棄しない。- プログラマティックエラーにのみ
Errorを実装するオブジェクトをthrowする。 Errorまたはそれを実装する型を明示的にcatchしない。- キャッチした例外を再throwするために
rethrowを使用する。
非同期処理
- 生のfutureを使用するよりもasync/awaitを推奨する。
- 有用な効果がない場合は
asyncを使用しない。 - ストリームを変換するために高階メソッドの使用を検討する。
- Completerを直接使用することを避ける。
- 型引数が
ObjectになりうるFutureOr<T>を曖昧化するときはFuture<T>をテストする。
設計
名前
- 用語を一貫して使用する。
- 略語を避ける。
- 最も記述的な名詞を最後に置くことを推奨する。
- コードを文のように読めるようにすることを検討する。
- 非ブールプロパティまたは変数には名詞句を推奨する。
- ブールプロパティまたは変数には非命令動詞句を推奨する。
- 名前付きブールパラメーターでは動詞の省略を検討する。
- ブールプロパティまたは変数には「肯定的な」名前を推奨する。
- 主な目的が副作用である関数またはメソッドには命令動詞句を推奨する。
- 値を返すことが主な目的である関数またはメソッドには名詞句または非命令動詞句を推奨する。
- 実行する作業に注意を向けたい場合は、関数またはメソッドに命令動詞句を検討する。
- メソッド名を
getで始めることを避ける。 - オブジェクトの状態を新しいオブジェクトにコピーする場合は、メソッド名を
to...()にすることを推奨する。 - 元のオブジェクトに支えられた異なる表現を返す場合は、メソッド名を
as...()にすることを推奨する。 - 関数またはメソッドの名前でパラメーターを説明することを避ける。
- 型パラメーターを命名するときは既存のニーモニック規則に従う。
ライブラリ
- 宣言をプライベートにすることを推奨する。
- 同じライブラリで複数のクラスを宣言することを検討する。
クラスとmixin
- 単純な関数で済む場合は1メンバーの抽象クラスを定義することを避ける。
- 静的メンバーのみを含むクラスを定義することを避ける。
- サブクラス化を意図していないクラスを拡張することを避ける。
- クラスが拡張できるかどうかを制御するためにクラス修飾子を使用する。
- インターフェースとして意図されていないクラスを実装することを避ける。
- クラスがインターフェースになれるかどうかを制御するためにクラス修飾子を使用する。
mixin classよりも純粋なmixinまたは純粋なclassを定義することを推奨する。
コンストラクター
- クラスがサポートしている場合はコンストラクターを
constにすることを検討する。
メンバー
- フィールドとトップレベル変数を
finalにすることを推奨する。 - 概念的にプロパティにアクセスする操作にはgetterを使用する。
- 概念的にプロパティを変更する操作にはsetterを使用する。
- 対応するgetterなしにsetterを定義しない。
- オーバーロードを偽装するためにランタイム型テストを使用することを避ける。
- 初期化子なしのパブリック
late finalフィールドを避ける。 - null可能な
Future、Stream、コレクション型を返すことを避ける。 - 流暢なインターフェースを有効にするためだけにメソッドから
thisを返すことを避ける。
型
- 初期化子なしの変数に型注釈を付ける。
- 型が明らかでない場合はフィールドとトップレベル変数に型注釈を付ける。
- 初期化されたローカル変数に冗長に型注釈を付けない。
- 関数宣言の戻り値の型に注釈を付ける。
- 関数宣言のパラメーター型に注釈を付ける。
- 関数式の推論されたパラメーター型に注釈を付けない。
- 初期化フォーマルに型注釈を付けない。
- 推論されないジェネリック呼び出しに型引数を書く。
- 推論されるジェネリック呼び出しに型引数を書かない。
- 不完全なジェネリック型を書くことを避ける。
- 推論が失敗する代わりに
dynamicで注釈を付ける。 - 関数型注釈でシグネチャを推奨する。
- setterに戻り値の型を指定しない。
- レガシーtypedef構文を使用しない。
- typedefよりもインライン関数型を推奨する。
- パラメーターには関数型構文を使用することを推奨する。
- 静的チェックを無効にしたい場合を除き、
dynamicを使用することを避ける。 - 値を生成しない非同期メンバーの戻り値の型として
Future<void>を使用する。 - 戻り値の型として
FutureOr<T>を使用することを避ける。
パラメーター
- 位置的ブールパラメーターを避ける。
- ユーザーが前のパラメーターを省略したい場合があるなら、オプション位置的パラメーターを避ける。
- 特別な「引数なし」値を受け入れる必須パラメーターを避ける。
- 範囲を受け入れるために包含的開始と排他的終了パラメーターを使用する。
等価性
==をオーバーライドする場合はhashCodeをオーバーライドする。==演算子を等価性の数学的ルールに従わせる。- ミュータブルクラスにカスタム等価性を定義することを避ける。
==のパラメーターをnull可能にしない。
Flutter アーキテクチャ推奨事項
このページでは、アーキテクチャのベストプラクティス、それらが重要である理由、そしてFlutterアプリケーションに推奨するかどうかを示します。 これらの推奨事項は推奨であって厳格なルールではないものとして扱い、 アプリの独特な要件に適応させるべきです。
このページのベストプラクティスには優先順位があり、 これはFlutterチームがどの程度強く推奨するかを反映しています。
- 強く推奨: 新しいアプリケーションの構築を開始する場合は、常にこの推奨事項を実装すべきです。現在のアプローチと根本的に衝突しない限り、この実践を実装するために既存のアプリをリファクタリングすることを強く検討すべきです。
- 推奨: この実践はアプリを改善する可能性が高いです。
- 条件付き: この実践は特定の状況下でアプリを改善できます。
関心の分離
アプリをUIレイヤーとデータレイヤーに分離すべきです。それらのレイヤー内で、責任によってロジックをクラスに更に分離すべきです。
明確に定義されたデータとUIレイヤーを使用する。
強く推奨
関心の分離は最も重要なアーキテクチャ原則です。 データレイヤーはアプリケーションデータをアプリの他の部分に公開し、アプリケーションのほとんどのビジネスロジックを含みます。 UIレイヤーはアプリケーションデータを表示し、ユーザーからのユーザーイベントをリッスンします。UIレイヤーには、UIロジックとウィジェット用の別々のクラスが含まれます。
データレイヤーでリポジトリパターンを使用する。
強く推奨
リポジトリパターンは、データアクセスロジックをアプリケーションの他の部分から分離するソフトウェア設計パターンです。 アプリケーションのビジネスロジックと基礎となるデータストレージメカニズム(データベース、API、ファイルシステムなど)の間に抽象化レイヤーを作成します。 実際には、これはRepositoryクラスとServiceクラスを作成することを意味します。
UIレイヤーでViewModelとViewを使用する。(MVVM)
強く推奨
関心の分離は最も重要なアーキテクチャ原則です。 この特定の分離により、ウィジェットが「愚か」なままであるため、コードのエラー率が大幅に減少します。
ウィジェットの更新を処理するためにChangeNotifierとListenableを使用する。
条件付き
状態管理を処理する多くのオプションがあり、最終的に決定は個人的な好みによります。
ChangeNotifier APIはFlutter SDKの一部であり、ウィジェットがViewModelの変更を観察する便利な方法です。
ウィジェットにロジックを置かない。
強く推奨
ロジックはViewModelのメソッドにカプセル化されるべきです。ビューが含むべきロジックは以下のみです:
- ViewModelのフラグまたはnull可能フィールドに基づいてウィジェットを表示・非表示にする単純なif文
- ウィジェットが計算に依存するアニメーションロジック
- 画面サイズや向きなどのデバイス情報に基づくレイアウトロジック
- 単純なルーティングロジック
ドメインレイヤーを使用する。
条件付き
複雑なロジック要件を持つアプリで使用。
ドメインレイヤーは、アプリケーションにViewModelを混雑させる非常に複雑なロジックがある場合、 またはViewModel内でロジックを繰り返していることに気づいた場合にのみ必要です。 非常に大きなアプリでは、ユースケースは有用ですが、ほとんどのアプリでは不必要なオーバーヘッドを追加します。
データの処理
データを注意深く処理することで、コードがより理解しやすく、エラーが発生しにくくなり、 不正な形式や予期しないデータが作成されることを防ぎます。
単方向データフローを使用する。
強く推奨
データ更新はデータレイヤーからUIレイヤーのみに流れるべきです。 UIレイヤーでのインタラクションは、処理されるデータレイヤーに送信されます。
ユーザーインタラクションからのイベントを処理するためにCommandを使用する。
推奨
コマンドはアプリでのレンダリングエラーを防止し、UIレイヤーがデータレイヤーにイベントを送信する方法を標準化します。
不変データモデルを使用する。
強く推奨
不変データは、必要な変更が通常はデータまたはドメインレイヤーの適切な場所でのみ発生することを保証するために重要です。 不変オブジェクトは作成後に変更できないため、変更を反映するために新しいインスタンスを作成する必要があります。 このプロセスはUIレイヤーでの偶発的な更新を防ぎ、明確な単方向データフローをサポートします。
不変データモデルを生成するためにfreezedまたはbuilt_valueを使用する。
推奨
データモデルで有用な機能を生成するためにパッケージを使用できます。freezedまたはbuilt_valueです。
これらは、JSON ser/des、深い等価チェック、コピーメソッドなどの一般的なモデルメソッドを生成できます。
これらのコード生成パッケージは、多くのモデルがある場合、アプリケーションのビルド時間を大幅に追加する可能性があります。
別々のAPIモデルとドメインモデルを作成する。
条件付き
大きなアプリで使用。
別々のモデルを使用すると冗長さが増しますが、ViewModelとユースケースの複雑さを防ぎます。
アプリ構造
よく整理されたコードは、アプリ自体の健全性とコードに取り組むチームの両方に利益をもたらします。
依存性注入を使用する。
強く推奨
依存性注入は、アプリにグローバルにアクセス可能なオブジェクトを持つことを防ぎ、コードのエラー率を下げます。
依存性注入を処理するためにproviderパッケージの使用を推奨します。
ナビゲーションにgo_routerを使用する。
推奨
Go_routerは、Flutterアプリケーションの90%を記述する推奨方法です。
go_routerが解決しない特定のユースケースがいくつかあります。
その場合は、Flutter Navigator APIを直接使用するか、pub.devで見つかる他のパッケージを試すことができます。
クラス、ファイル、ディレクトリに標準化された命名規則を使用する。
推奨
それらが表すアーキテクチャコンポーネントに基づいてクラスに名前を付けることを推奨します。 例えば、以下のクラスがあるかもしれません:
- HomeViewModel
- HomeScreen
- UserRepository
- ClientApiService
明確にするために、Flutter SDKのオブジェクトと混同される可能性のある名前は使用しないことを推奨します。
例えば、共有ウィジェットを/widgetsというディレクトリではなく、
ui/core/というディレクトリに配置すべきです。
抽象リポジトリクラスを使用する
強く推奨
リポジトリクラスはアプリ内のすべてのデータの信頼できる情報源であり、 外部APIとの通信を促進します。 抽象リポジトリクラスを作成することで、「development」や「staging」などの 異なるアプリ環境で使用できる異なる実装を作成できます。
テスト
良いテスト慣行はアプリを柔軟にします。 また、新しいロジックと新しいUIを追加することを簡単で低リスクにします。
アーキテクチャコンポーネントを個別にそして一緒にテストする。
強く推奨
- すべてのサービス、リポジトリ、ViewModelクラスに対してユニットテストを書く。これらのテストは、すべてのメソッドのロジックを個別にテストすべきです。
- ビューに対してウィジェットテストを書く。ルーティングと依存性注入のテストは特に重要です。
テスト用のフェイクを作成する(そしてフェイクを利用するコードを書く)。
強く推奨
フェイクは特定のメソッドの内部動作よりも 入力と出力に関心があります。アプリケーションコードを書く際にこれを念頭に置くと、 明確に定義された入力と出力を持つモジュラーで軽量な関数とクラスを書くことが強制されます。