Flutterの状態管理について
StatefullWidgetとStatelessWidget
これらはFlutterの状態管理において最も入門的な方法です。
StatefulWidgetはミュータブルな状態を持つことができるWidgetです。
また他のWidgetをまとめるための役割も持ちます。
StatelessWidgetはイミュータブルば状態を持つことができるWidgetです。
これもStatefulWidgetと同様に他のWidgetをまつめる役割を持ちます。
StatefulWidgetとStatelessWidgetの大きな違いは、前述の通りミュータブルな状態を持つかイミュータブルな状態を持つかです。
それぞれのWidgetを用いてコードを組んでみます。
例題としてテキストフィールドに文字を入力する度に文字列を変数に格納していきます。
画面間で状態を共有したい場合、StatefulWidgetだと実現するのが難しい。
=> StatefulWidget + ChangeNotifierを使えば解決される?
StatelessWidgetの場合
まずはStatelessWidgetで組んでみます。
class SampleApp extends StatelessWidget {
final String enteredText;
const SampleApp({Key? key, String? text}) : enteredText = text ?? "", super(key: key);
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Description',
),
initialValue: enteredText,
onChanged: (String? value) {
if (value != null) {
enteredText = value;
}
},
);
}
}
上記のコードはコンパイルエラーになります。
理由としてはStatelessWidgetはイミュータブルでなければならないためです。
StatelessWidgetのスパークラスであるWidget
クラスは、
@immutable
abstract class Widget extends DiagnosticableTree
と定義されています。重要なのは@immutable
というアノテーションです。
このアノテーションをつけたクラスはイミュータブルなクラスとしての要件を満たしていないと警告をだしてくれます。
先程のコードの場合だと、
final String enteredText;
でfinal宣言しているので値を変更することはできないにも関わらず、
enteredText = value;
で値を変更しようとしているためにコンパイルエラーになってしまいました。
またfinal
宣言を外してもイミュータブルなクラスではなくなってしまうので、これもまたコンパイルエラー になってしまいます。
ではStatelessWidgetが適した場面はどのようなときなのでしょうか?
それは状態が一切変更する可能性がない場合のときであり、
-
MaterialApp
のような、アプリのルートクラス - 設定画面
- リストのアイテム
などが該当すると思います。
StatefulWidgetの場合
次はStatefulWidgetです。
StatefulWidgetはStatelessWidget同様、Widget
クラスを継承しているので実はイミュータブルなクラスです。ただStatelessWidgetと異なるのはState
の存在です。State<T>を継承したクラス側にイミュータブルな状態を持つように実装していきます。
class SampleApp extends StatefulWidget {
final String text;
const SampleApp({Key? key, String? text}) : text = text ?? "", super(key: key);
_SampleAppState createState() => _SampleAppState();
}
class _SampleAppState extends State<SampleApp> {
String enteredText = "";
Widget build(BuildContext context) {
enteredText = widget.text;
return Scaffold(
appBar: AppBar(
title: const Text('テスト'),
),
body: TextFormField(
decoration: const InputDecoration(
labelText: 'Description',
),
initialValue: enteredText,
onChanged: (String? value) {
if (value != null) {
enteredText = value;
print(enteredText);
}
},
),
);
}
}
State
はイミュータブルな状態を持っていますが、コンパイルエラーは起きず文字を入力するたびに文字列が出力されます。このように状態が変更する可能性がある画面については、StatefulWidgetで実装します。
StatefulWidgetの問題点
StatefulWidgetで実装してみて、状態の共有が難しいなと感じました。
状態の共有とは、画面間で同じデータを参照する、というようなことです。