Closed5

Flutterの状態管理について

Shun UematsuShun Uematsu

StatefullWidgetとStatelessWidget

これらはFlutterの状態管理において最も入門的な方法です。

StatefulWidgetはミュータブルな状態を持つことができるWidgetです。
また他のWidgetをまとめるための役割も持ちます。

StatelessWidgetはイミュータブルば状態を持つことができるWidgetです。
これもStatefulWidgetと同様に他のWidgetをまつめる役割を持ちます。

StatefulWidgetとStatelessWidgetの大きな違いは、前述の通りミュータブルな状態を持つかイミュータブルな状態を持つかです。

それぞれのWidgetを用いてコードを組んでみます。
例題としてテキストフィールドに文字を入力する度に文字列を変数に格納していきます。

画面間で状態を共有したい場合、StatefulWidgetだと実現するのが難しい。
=> StatefulWidget + ChangeNotifierを使えば解決される?

Shun UematsuShun Uematsu

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のような、アプリのルートクラス
  • 設定画面
  • リストのアイテム
    などが該当すると思います。
Shun UematsuShun Uematsu

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で実装します。

Shun UematsuShun Uematsu

StatefulWidgetの問題点

StatefulWidgetで実装してみて、状態の共有が難しいなと感じました。
状態の共有とは、画面間で同じデータを参照する、というようなことです。

このスクラップは2021/12/09にクローズされました