Help us understand the problem. What is going on with this article?

[Flutter] FutureBuilderを使って非同期でWidgetを生成する

はじめに

最近Flutterはじめました。
FutureBuilderはとてもシンプルで使いやすい機能ですが、初見で何やってるのかピンとこなかったのでまとめます。

2019/12/15 追記: かなり雑だったので少し修正しました。

まずはFutureBuilderを使って表示するためのデータを返してくれるメソッドをテスト用に作成しましょう。
_getFutureValue()です、APIで通信して文字列を取得するケースを擬似的に再現しています。
1秒後にFuture型の"データの取得に成功しました"という文字列を返します。

  Future<String> _getFutureValue() async {
    // 擬似的に通信中を表現するために1秒遅らせる
    await Future.delayed(
      Duration(seconds: 1),
    );
    return Future.value("データの取得に成功しました");
  }

FutureBuilderを追加 

次は本来表示したいWidgetの階層にFutureBuilderを追加します、今回はCenter直下です。

futureの引数に先程作った_getFutureValue()を代入、
builderには(BuildContext context, AsyncSnapshot snapshot) { }

futureにセットしたメソッドがreturnしてくれる値(今回の場合は"データの取得に成功しました")をsnapshot.dataで取得できます。

しかし、ただbuilder内でreturn Text(snapshot.data);としてしまうとエラーになってしまいます。
最初の1秒間は_getFutureValue()は何もデータを返さないのでsnapshot.dataがnulになってしまうからです。

snapshotに本当にデータが格納されているかをsnapshot.hasDataで確認してからreturnしましょう。

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FutureBuilder Demo'),
      ),
      body: Center(
        child: FutureBuilder(
          future: _getFutureValue(),
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) { 
            if (snapshot.hasData) {
              return Text(snapshot.data);
            } else {
              return Text("データが存在しません");
            }
          },
        ),
      ),
    );
  }

builderを(BuildContext context, AsyncSnapshot<指定したい型> snapshot)とすればsnapshot.dataの型を指定できます。

通信状態を考慮する

上記のケースだとデータがあるかないかの分岐しかないので
通信中でデータがない場合でも、通信完了したがデータの取得には失敗した場合でも"データが存在しません"とだけ表示されてしまいます。

通信が完了するまではスピナーを表示させましょう。
通信状態はsnapshot.connectionStateで取得できます
ConnectionStateがdoneになるまではCircularProgressIndicatorを表示させます。

            if (snapshot.connectionState != ConnectionState.done) {
              return CircularProgressIndicator();
            }

エラーハンドリング

通信に失敗した場合などを想定しましょう。

まずは先程作った_getFutureValue()でExceptionを返すように修正します

  Future<String> _getFutureValue() async {
    // 擬似的に通信中を表現するために1秒遅らせる
    await Future.delayed(
      Duration(seconds: 1),
    );

    try {
      // 必ずエラーを発生させる
    throw Exception("データの取得に失敗しました");
    } catch (error) {
      return Future.error(error);
    }
  }

エラーかどうかはsnapshot.hasErrorで、
エラーメッセージはsnapshot.errorで取得できます。
エラーの場合はエラーメッセージを表示させるようにしましょう。

            if (snapshot.hasError) {
              return Text(snapshot.error.toString());
            }

ここまで全ての実装でbuildメソッド全体はこのようになります

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FutureBuilder Demo'),
      ),
      body: Center(
        child: FutureBuilder(
          future: _getFutureValue(),
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
            // 通信中はスピナーを表示
            if (snapshot.connectionState != ConnectionState.done) {
              return CircularProgressIndicator();
            }

            // エラー発生時はエラーメッセージを表示
            if (snapshot.hasError) {
              return Text(snapshot.error.toString());
            }

            // データがnullでないかチェック
            if (snapshot.hasData) {
              return Text(snapshot.data);
            } else {
              return Text("データが存在しません");
            }
          },
        ),
      ),
    );
  }


さいごに

正直Dart良くわかってない感まだあるので間違ってたらぜひおしえてください。

コード全体はこちらです

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした