💭

【Flutter Widget of the Week #6】FutureBuilderを使ってみた

2022/10/02に公開

はじめに

Flutter Widget of the Week #6 FutureBuilder についてまとめましたので、紹介します。
https://youtu.be/ek8ZPdWj4Qo

FutureBuilderとは

FutureBuilder は非同期処理で Widget を生成したいときに使う Widget です。
そもそも非同期処理とはなんでしょうか?
非同期処理の反対の同期処理との違いを確認しながら説明します。
同期処理・非同期処理

まず同期処理は簡単に言うと、書かれたプログラムが1行ずつ順番に実行される処理のことです。
上図の左のようにタスク1とタスク2の処理は順番に実行され、
一つ一つの処理が終わるまで次の処理が実行されることはありません。
処理の動きが分かりやすいメリットがある一方、
処理が終わるまで他の処理が開始されないので処理待ちが発生しやすいデメリットがあります。

アプリの画面は一般的にサーバーなどからデータを読み込んだあと、
取得したデータをもとに画面を構築、表示しています。
もし、データの読み込みが重く、時間がかかる場合、
データの読み込みが終わるまで画面が表示されないことになります。
そうすると、アプリを使うユーザーからしたら画面が固まったように見えて、
ストレスになってしまうかもしれません。
それを解消したのが非同期処理です。

非同期処理はあるタスクを実行している間でも、
その処理を止めずに別のタスクをを実行できる処理のことです。
上図の右のようにタスク2の処理が実行している間でもその処理の結果を待たずにタスク1の処理を実行できます。
そのため、処理効率が上がり、処理待ちによるユーザーストレスを解消することができるのです。
その一方でプログラムが複雑化しやすくなるので注意が必要です。

FutureBuilder Sampleプログラム

次にFutureBuilderを実際に動かしてみましょう。
コードはFlutterの API Document にあるものです。
実行画面としては起動後すぐに「Awaiting Result...」と結果待ち状態の画面が表示され、2秒後に「Result: Data Loaded」と結果待ち終了の画面が表示されます。

結果待ち状態の画面

結果待ち状態の画面

結果待ち終了後の画面

結果待ち終了後の画面

※右下の Return ボタンをタップすると再度実行されます。

main.dart
class _FutureBuilderSampleState extends State<FutureBuilderSample> {
  final Future<String> _calculation = Future<String>.delayed(
    const Duration(seconds: 2),
    () => 'Data Loaded',
  );

  
  Widget build(BuildContext context) {
    return DefaultTextStyle(
      style: Theme.of(context).textTheme.headline2!,
      textAlign: TextAlign.center,
      child: FutureBuilder<String>(
        future: _calculation, // Future<T> 型を返す非同期処理
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          List<Widget> children;
          if (snapshot.hasData) { // 値が存在する場合の処理
            children = <Widget>[
              const Icon(
                Icons.check_circle_outline,
                color: Colors.green,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Result: ${snapshot.data}'),
              ),
            ];
          } else if (snapshot.hasError) {// エラーが発生した場合の処理
            children = <Widget>[
              const Icon(
                Icons.error_outline,
                color: Colors.red,
                size: 60,
              ),
              Padding(
                padding: const EdgeInsets.only(top: 16),
                child: Text('Error: ${snapshot.error}'),
              ),
            ];
          } else { // 値が存在しない場合の処理
            children = const <Widget>[
              SizedBox(
                width: 60,
                height: 60,
                child: CircularProgressIndicator(),
              ),
              Padding(
                padding: EdgeInsets.only(top: 16),
                child: Text('Awaiting result...'),
              ),
            ];
          }
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: children,
            ),
          );
        },
      ),
    );
  }
}

FutureBuilder のプロパティについて

FutureBuilder に使われているプロパティは future と builder の二つがあります。

①future

Future<T> 型を返す非同期処理が設定される
今回は Future<String> 型が設定されています。

②builder

引数に BuildContext 型の context と AsyncSnapshot<T> 型の snapshot を持ち、
future にセットしたメソッドが返した値を snapshot.data で取得できます。

③initialData

null 以外の Future が完了するまで渡される値を initialData で設定できます。
今回は initialData は使わず、 snapshot.hasData でif-else の条件分岐を使うことで Future が完了するまでの処理が作られています。

AsyncSnapshot<T> snapshot のもつ機能

shanpshot は機能をいくつか持っているので紹介します。

①data

FutureBuilder の future プロパティに設定した非同期処理の結果を出力する

②hasData

data に値があるかどうかを true/false で出力する
非同期処理の結果が返ってきたかを判断する際に使われる

③error

非同期処理でエラーが発生したときのエラー情報を出力する

④hasError

error に値があるかどうかを true/false で出力する
非同期処理でエラーがあったかを判断する際に使われる

⑤connectionState

非同期処理の処理状態を出力する
ConnectionState 型が使われる

最後に

今回は FutureBuilder を紹介しました。非同期処理の処理状態によって表示を変えることはアプリを作っているとよくあります。そのときに今回学んだ FutureBuilder を使うと便利ですね。
次は #7 FadeTransition です。またお会いしましょう。

参考記事

https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Discussion