こんにちは、Habr! 私の記事の翻訳を
提示します。アプリの状態管理ソリューションを理解して選択するのを手伝ってください 。 この翻訳に関する批判を聞いてうれしいです。 バッククォート( ``)には、私の個人的な考えや説明が書かれています。
フラッター状態管理はホットなトピックです。 問題には多くの解決策がありますが、それらに迷い込んで、あなたのニーズに最も適したものを選ぶことは非常に簡単です。 私自身は混乱していましたが、適切な解決策を見つけました。 それをあなたと共有させてください。
ニーズに合ったソリューションを見つけるには、ニーズ自体を判断する必要があります。 私の場合、これは次のとおりです。
- コードの品質を損なうことなくプロジェクトを開発する機会があります
- 表示ロジックをビジネスロジックから分離する
- 壊れにくい明確なコードを持っている
- コードの予測可能性と理解可能性
これらの要件を考慮すると、適切なオプションは残ります。
setState()
メソッドとステートフルウィジェットの使用- `ライブラリ` ScopedModel
- BLoCパターンの使用(ビジネスロジックコンポーネント)
- Redux
ローカル状態とグローバル状態の違い
選択したソリューションの分析に飛び込む前に、ローカル状態とグローバル状態の違いを理解する必要があります。 実際の例はこれに適しています:
ユーザーがユーザー名とパスワードの入力を求められ、フォームを送信した後に「ユーザーID」オブジェクトを取得する認証フォームを想像してください。 この例では、フォームフィールドに入力されたデータの検証は「承認フォームウィジェット」のローカル状態の一部となり、アプリケーションの残りの部分はこれを認識すべきではありません。 また、「認可サーバー」によって返される「アイデンティティ」オブジェクトは、グローバル状態の一部です。 したがって、他のコンポーネントはこのオブジェクトに依存し、ユーザーが許可されているかどうかに応じて動作を変更します。
待つことにうんざりしている人のための簡単な結論待ちたくない場合、または私の研究に興味がない場合は、結果の概要を以下に示します。
特に、時間の経過とともに成長する複雑なアプリケーションを構築している場合は、ローカル状態管理にBLoCを、グローバル状態にReduxを使用することをお勧めします。
setState()を使用すべきではない理由
ウィジェット内で
setState()
を使用すると、これらの変更のプロトタイプをすばやく作成してフィードバックを得るのに最適ですが、表示ロジックがビジネスロジックと混ざり合っており、コードの清潔さと品質の原則に違反しているため、この方法では目標を達成できません。 このようなコードのメンテナンスは将来困難になるため、プロトタイピングを除き、このアプローチは推奨されません。
ScopedModel-正しい方向への一歩
ScopedModelは、
Brian Eganのサードパーティライブラリです。 特別な
Models
オブジェクトを作成し、必要に応じて
notifyListeners()
メソッドを使用することができます。 たとえば、モデルオブジェクトのプロパティの変更を追跡するには:
class CounterModel extends Model { int _counter = 0; int get counter = _counter; void increment() { _counter++; notifyListeners(); } }
ウィジェットでは、
ScopedModelDescendant
このライブラリが提供する」
ScopedModelDescendant
ウィジェットを使用して、モデルの変更に応答できます。
class CounterApp extends StatelessWidget { @override Widget build(BuildContext context) { return new ScopedModel<CounterModel>( model: new CounterModel(), child: new Column(children: [ new ScopedModelDescendant<CounterModel>( builder: (context, child, model) => new Text('${model.counter}'), ), new Text(" , CounterModel") ]) ); } }
setState()
アプローチの使用とは対照的に、このソリューションでは、ビジネスロジックから表示ロジックを分離できます。 ただし、特定の制限が課されます。
Model
が複雑になると、 notifyListeners()
メソッドをnotifyListeners()
使用するかを決定することが難しくなります。Model
提供するAPIは、一般に、アプリケーションインターフェイスの非同期性を正確に記述していません
これらすべてを考慮すると、アプリケーションの状態を管理するのが容易でない場合、このアプローチの使用はお勧めしません。 彼がアプリケーションの成長と複雑さを生産的に提供できるとは信じていません。
強力なソリューション-BLoC
このパターンはGoogleによって考案され、そこでも使用されています。 彼は私たちが次の目標を達成するのを助けてくれます。
- 表示ロジックとビジネスロジックの分離
- 非同期性を使用してインターフェイスを表示する
- FlutterやAngularDartなどのさまざまなDartアプリケーションで再利用する機能
このアプローチの背後にある考え方は非常に簡単です。
- BLoCが使用する
Sink<T>
データコンポーネントを非同期的に入力するためのAPI - BLoCが使用する
Stream<T>
コンポーネントによって非同期的に返されるデータを記述するAPI - 最後に、
StreamBuilder
ウィジェットを使用してデータフローを制御できます。データの更新をサブスクライブし、ウィジェットを再描画するために私たちの側で努力StreamBuilder
必要はありません
Googleはこの状態管理パターンを使用する良い例があります。これは、この状態管理パターンが広く使用されており、会社によって強く推奨されているためです。
私はこのアプローチを使用してローカル状態を管理することを強くお勧めしますが、グローバル状態の管理にも適しています。 ただし、後者の場合、問題が発生します-BLoCをどこでどのように正しく実装して、さまざまなコンポーネントがBLoCにアクセスし、Reduxがシーンに入るか。
ReduxとBLoC-私にぴったりのミックス
記事の冒頭で説明した目標の1つは、広く使用され予測可能なものを見つけることでした。これはReduxです。これは、グローバルステートの管理に役立つパターンとツールのセットです。 コアには3つの基本原則があります。
唯一の真実のソースは 、アプリケーションのすべての状態が単一の
store
内のツリーオブジェクトに格納されることです。
- 状態は読み取り専用です-状態を変更する唯一の方法は、状態に何が起こるべきかを記述する特別なアクションオブジェクトを呼び出すことです
- 変更は純粋な関数を使用して行われます-状態の変化を判断するには、純粋な `
reducer
関数をreducer
ます。これは副作用を引き起こさないはずです` サンプルコードへのリンク `
画像の元の投稿へのリンク状態を管理するこのアプローチは、Web開発者に広く受け入れられており、モバイルデバイスに登場することで、Web開発者やモバイルアプリケーション開発者のメリットが得られます。
ブライアン・イーガンは、オリジナルの
Reduxと
flutter_reduxの両方を開発してい
ます 。また、Reduxを含む多くのアーキテクチャパターンを適用した素晴らしい
Todoアプリケーションも作成しました。
Reduxのすべての特性を考えると、グローバルステートの管理に使用することを強くお勧めしますが、アプリケーションをスケーリングする場合は、ローカルステートの管理には使用しないでください。
最後の言葉
この記事には、完全に正しいまたは間違った解決策はありません。 プロジェクトに適用するアプローチを決定するには、ニーズを決定する必要があります。 私と私の目標にとって、ReduxとBLoCの組み合わせにより、プロジェクトを迅速かつ安全に成長させることができ、また、アクセスしやすく明確なツールのおかげで、サードパーティ開発者がこれらのプロジェクトに参加しやすくなります。 しかし、誰もが同じニーズを持っているわけではなく、時間が経つにつれて「現在のツール」の問題とさらに良い解決策の両方を見つけることができます。 常に好奇心を持ち、このツールまたはそのツールがあなたに適しているかどうかを学び、考えることが非常に重要です。