いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Flutterメモ-06 (StatefulWidget)

概要

今回は StatefulWidget つまり、「状態を持つウィジェット」について。

Flutterのバージョン

$ flutter --version
Flutter 2.2.3 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f4abaa0735 (3 weeks ago) • 2021-07-01 12:46:11 -0700
Engine • revision 241c87ad80
Tools • Dart 2.13.4

StatelessWidget については以下を参照ください。

devlights.hatenablog.com

StatefulWidget は、その名前のとおり「状態を持つウィジェット」ですね。

どのUIフレームワークでも同じですが、状態が変化したらUIを更新するのが常です。

なので、状態を持つウィジェットというのは、何かのデータが変わったらUIを更新する必要がある部分に使います。

StatefulWidgetの公式ドキュメントは以下です。

api.flutter.dev

ページの途中からパフォーマンスに関する記載がいっぱい書いてあります。このような記述が公式のドキュメントで記載されているのはとても素晴らしいですね。

でも、最初からパフォーマンスを意識して書こうとしても難しいので、今はそういうのは考えずに行きます。というか私がまだそこまで理解できていない。。

とりあえず、StatefulWidgetを使う上で覚えておくべきは以下の項目です。

  • StatefulWidgetを継承したウィジェットにする
  • Stateを継承したクラスを一緒に用意する
  • StatefulWidgetまたはStateの中で setState() を呼ぶと State の build メソッドが呼ばれてUIを更新できる
    • initStateメソッドでデータの初期化
    • disposeメソッドでデータの後始末
    • buildメソッドでUIを構築

サンプル

UIを勉強するときに、データを変化させていくサンプルでちょうどいいのが時計さんです。

flutter create すると出力されるサンプルもいいのですが、データが変わったら自動でUIが変化していく方が「動いてる感」が出て個人的に好きです。

main.dart

import 'package:flutter/material.dart';

import 'app.dart';

void main() {
  runApp(App());
}

app.dart

import 'package:flutter/material.dart';

import 'screens/clock_screen.dart';

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Basic-03 StatefulWidget',
      theme: ThemeData.dark(),
      home: ClockScreen(),
    );
  }
}

screens/clock_screen.dart

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';

/// [StatefulWidget]を継承したウィジェット
///
/// [StatefulWidget]を継承した場合[createState]メソッドをオーバーライドする
/// 必要がある.このメソッドからは自身に対応する[State]オブジェクトを生成して返す.
class ClockScreen extends StatefulWidget {
  @override
  _ClockScreenState createState() {
    return _ClockScreenState(DateTime.now());
  }
}

/// [ClockScreen]に対応する[State]オブジェクト
///
/// このクラスで状態管理を行いながらUIを処理する.
///
/// 主に利用する親クラスのメソッドは以下
///
/// - initState
/// - dispose
/// - build
///
/// [setState]を呼び出すと、buildメソッドが呼び出される.
/// なので、UIを反映したくなったら[setState]を呼び出せば良い.
/// [setState]の引数にコールバックを渡すことができるので
/// この中でUI変更に関わる値を変更する.
///
class _ClockScreenState extends State<ClockScreen> {
  DateTime dt;

  late DateFormat _formatter;
  late Timer _timer;

  _ClockScreenState(this.dt);

  @override
  void initState() {
    _formatter = DateFormat("HH:mm:ss");

    // 1秒毎に着火するタイマーで setState を呼び出して UI を更新
    _timer = Timer.periodic(Duration(seconds: 1), (t) {
      setState(() {
        // このコールバックの処理が終わると build メソッドが呼び出される
        dt = DateTime.now();
      });
    });

    super.initState();
  }

  @override
  void dispose() {
    if (_timer.isActive) {
      _timer.cancel();
    }

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
          child: Text(
        _formatter.format(dt),
        style: Theme.of(context).textTheme.headline2,
      )),
    );
  }
}

libディレクトリの下は以下のような感じ。

f:id:gsf_zero1:20210729012219p:plain

実行すると以下のようになります。

f:id:gsf_zero1:20210729012935p:plain

何の変哲もなくチクタクと更新しているだけですが、状態が更新されて (setState() が呼ばれて)、UIが再描画されて (build() が呼ばれて) いることがわかります。

参考情報

Flutter は本家のドキュメントだけでなく、YouTube上での動画も沢山用意されているので、リソースには困りませんね。めっちゃ分かりやすいです。

youtu.be

youtu.be

api.flutter.dev


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com