はじめに
最近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良くわかってない感まだあるので間違ってたらぜひおしえてください。