今回はFlutterで画面上のWidgetを非同期かつ動的に変化させたい際によく使うFutureBuilderについて解説していきます。
例えば、何かのAPIを叩く処理があったとして、その通信中にはローディングを表示させておいて、レスポンスが返って来たらその結果を画面に表示したいと言った場面はよくあると思います。
そんな時にFutureBuilderが便利です。
この記事を読み終わる頃にはFutureBuilderの便利さと使い方を理解できていると思いますので是非最後まで読んでみてください。
目次
使い方
早速実際にサンプルコードを見ながら説明していきますが、今回プログラムの状態管理はStatefulWidgetではなく、デファクトスタンダードになっているProviderというライブラリを使用していきます。
Providerに関しては「【Flutter】StatefulWidgetを使わずにProviderを使って効率的にWidgetの再描画をする」で解説しているので参考にしてみてください。
【Flutter】StatefulWidgetを使わずにProviderを使って効率的にWidgetの再描画をする
APIを叩く処理を再現して3秒ローディングを表示した後に画面を更新するだけの簡単プログラムです。
コピペですぐ動かせるので試してみてください。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'futurebuilder demo',
home: ChangeNotifierProvider<MainModel>(
create: (_) => MainModel(),
child: Scaffold(
appBar: AppBar(title: Text('FutureBuilder')),
body: Consumer<MainModel>(builder: (context, model, child) {
return Center(
child: FutureBuilder(
future: model.callApi(),
builder:
(BuildContext context, AsyncSnapshot<String> snapshot) {
// 通信中はローディングのぐるぐるを表示
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
// 通信が失敗した場合
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
// snapshot.dataにデータが格納されていれば
if (snapshot.hasData) {
return Center(child: Text(snapshot.error.toString()));
}
return Text('APIからデータが取れませんでした。');
},
),
);
}),
),
),
);
}
}
class MainModel extends ChangeNotifier {
Future<String> callApi() async {
Future hoge;
await Future.delayed(Duration(seconds: 3)); // 3秒遅延させる(APIを叩いてると仮定)
return '通信成功!';
}
}
要所解説
...
child: FutureBuilder(
future: model.callApi(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
...
FutureBuilderでは下記のプロパティが必須になっています。
future
futureプロパティには型がFutureの関数または変数などを指定します。
実際にはサンプルコードの様なAPIを叩く処理、あるいはDBと通信する等の非同期関数を指定することが多いです。
今回のサンプルコードでは非同期のAPI呼び出しを再現した関数を指定しています。
builder
builderプロパティには非同期関数の処理過程によって出し分けをしたいWidgetについての記載をしていきます。
futureに指定した関数の処理ステータスは第2引数に指定されているsnapshotに格納され、処理ステータスが変化するに応じて自動的にこのbuilderプロパティにネストされている部分が再ビルドされます。
このsnapshotオブジェクトに対して「.〇〇」と用意されているメソッドを使用することで、非同期処理の通信状態、関数からの返却値等を取得可能です。
例えばsnapshot.dataとするとfutureに指定した関数からの戻り値を取得できます。
ちなみにAsyncSnapshot<String>の「<String>」には返却されるsnapshotの型を指定できます。
...
// 通信中はローディングのぐるぐるを表示
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
...
通信中を検知する条件文です。
snapshot.connectionStateとすることでfutureに設定した関数等の通信状態を取得できます。
connectionStateの状態が通信中であればCircularProgressIndicator()をreturnしてローディング中を表現するスピナーを表示させます。
ちなみにif文にsnapshot.connectionState != ConnectionState.doneと記述しても「通信が未完了」という条件文になるので同じ動作を実現できます。ConnectionState.doneは通信完了のステータスです。
...
// 通信が失敗した場合
if (snapshot.hasError) {
return Center(
child: Text(snapshot.error.toString()),
);
}
...
エラーハンドリング用の条件文です。
snapshot.hasErrorでfutureに設定した関数等のエラー状態を判定できます。
更にsnapshot.errorとするとエラーメッセージが取得できます。
...
// snapshot.dataにデータが格納されていれば
if (snapshot.hasData) {
return Center(child: Text(snapshot.data));
}
return Text('APIからデータが取れませんでした。');
...
非同期関数から正常に値が返却された時用の条件文です。
snapshot.hasDataで正常に関数等からのレスポンスが返っているかを判定できます。
そのレスポンスされたデータ辞退はsnapshot.dataでアクセスできます。
おわりに
ここまでFutureBuilderの基本的な使い方を解説してきました。
APIを叩いたり非同期処理をする際はFutureBuilderを使う機会が多いと思うのでこの機会に是非習得してみてください。
他にもFlutterに関する記事を挙げているのでそちらも参考にしてみてください。
【Flutter初心者向け】ルーレットアプリを作って今晩の夕食を決めてみた【ソースコピペOK】【Flutter/アプリ開発】スクレイピングを利用して副業案件検索アプリを作ってみよう!
【Flutter】Widgetのサイズを変更できるSizedBoxの使い方【サンプルコードあり】