Flutterの学習で詰まるポイント(2)Stateがわからないで、Sateは、Widgetのライフサイクルを表現したものだと説明しました。

Flutterが提唱しているWidgetのライフサイクルは、生成→初期化→変更→破棄で表現できます。図式化したのが、こちらの画像です。

setState

上記の図の右側に注目してください。ready→(強制的にウイジェットを再構築)→ setState → buildと書かれていると思います。setStateを呼び出すことで、buildを呼び出すことができます。

floatingActionButton: FloatingActionButton(
      onPressed:() => setState( () => _counter++;),
      tooltip: 'Increment',
      child: const Icon(Icons.add),
  ),

setStateに引数も戻り値も持たない関数を代入して、関数の内部で自分が参照しているデータを更新します。そうすると、新しいデータでbuildが実行されます。build(1), build(2) というイメージを持つと、わかりやすいかもしれません。

カウンターアプリのコードにコメントをたくさん入れておくので、StatefulWidgetのコードを読めるようになってください。

import 'package:flutter/material.dart';

void main() {
  // Flutterアプリが起動する時に呼ばれる関数。必ず実装する。
  // runAppも必要。
  runApp(const CounterAppWidget(title: 'CounterApp'));
}

/// [StatefulWidget]を継承したクラスを作ります。
class CounterAppWidget extends StatefulWidget {
  const CounterAppWidget({required this.title, super.key});

  /// AppBarのタイトルです
  final String title;

  /// createStateの実装が必須です
  /// [CounterAppWidget]に紐づくStateが生成されます。
  @override
  State<CounterAppWidget> createState() => CounterAppState();
}

/// [CounterAppWidget]の状態管理をするためのStateを生成します。
class CounterAppState extends State<CounterAppWidget> {
  int _counter = 0;

  /// Widgetが初期化される時に呼び出したい処理を書きます
  /// ここでは複雑になるので書きませんが、APIからデータを取って仕込む等が考えられます。
  @override
  void initState() {
    super.initState();
  }

  // setStateが呼ばれると、buildメソッドが再実行されます。
  // counterがインクリメントされた状態でbuildされますので、
  // 1,2,3,4...とUIが更新されるという仕組みです。
  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'You have pushed the button this many times:',
              ),
              Text(
                '$_counter',
                style: Theme.of(context).textTheme.headlineMedium,
              ),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _incrementCounter,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}