【Flutter】StateにFutureを持たせた時のreload方法
最終更新から6年以上たっています。ライブラリのバージョンや手順が変わっている可能性があります。
こんにちは。趣味グラマのNobu(@nm_aru)です。
今回もFlutterの備忘録を残しておこうと思います。
StateのFutureをreloadする(失敗例)
build内でFutureBuilderを使い、そこで使用するデータとしてFuture<List<String>>みたいな物をStatefulWidgetのStateとして保持していました。
内容は変えていますが、以下のようなソースコードです。
import 'package:flutter/material.dart';
class ListViewWidget extends StatefulWidget {
@override
_ListViewWidgetState createState() => _ListViewWidgetState();
}
class _ListViewWidgetState extends State<ListViewWidget> {
Future<List<String>> _data;
@override
void initState() {
super.initState();
_data = HogeData().getHogeData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List'),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.swap_vert),
onPressed: () => _reload(),
),
body: FutureBuilder<List<String>>(
future: _data,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
break;
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: SizedBox(height: 50.0, width: 50.0, child: CircularProgressIndicator()));
case ConnectionState.done:
if (snapshot.hasError) {
return Text('${snapshot.error}');
}
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) => ListItem(snapshot.data[index]),
);
}
}
return null;
}),
);
}
void _reload() {
var data = HogeData().getHogeData();
setState(() => _data = data); // ★★ これだとエラーになる
}
}この状況で、FloatingActionButtonボタンを押すと、55行目で以下のエラーが発生します。
FlutterError (setState() callback argument returned a Future.
The setState() method on _ListViewWidgetState#3ce67 was called with a closure or method that returned a Future. Maybe it is marked as “async”.
Instead of performing asynchronous work inside a call to setState(), first execute the work (without updating the widget state), and then synchronously update the state inside a call to setState().)
エラーメッセージを見る限り、setState()の中で非同期のFutureを使っている事が問題のように見えます。
しかし、StateにFutureを持っているので、setStateを呼ばないとreloadされない…さてどうしたものかと色々試した結果、次のコードで無事解決しました。
StateのFutureをreloadする(成功例)
import 'package:flutter/material.dart';
class ListViewWidget extends StatefulWidget {
@override
_ListViewWidgetState createState() => _ListViewWidgetState();
}
class _ListViewWidgetState extends State<ListViewWidget> {
Future<List<String>> _data;
@override
void initState() {
super.initState();
_data = HogeData().getHogeData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('List'),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.swap_vert),
onPressed: () => _reload(),
),
body: FutureBuilder<List<String>>(
future: _data,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
break;
case ConnectionState.active:
case ConnectionState.waiting:
return Center(child: SizedBox(height: 50.0, width: 50.0, child: CircularProgressIndicator()));
case ConnectionState.done:
if (snapshot.hasError) {
return Text('${snapshot.error}');
}
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) => ListItem(snapshot.data[index]),
);
}
}
return null;
}),
);
}
void _reload() {
_data = HogeData().getHogeData();
setState(() {}); // ★これ!
}
}変更したのは54〜55行目になります。
要はStateのFutureに直接データを入れたあと、setState()をただ呼び出すだけです。
まとめ
この解決法がFlutter的に良いのかは分かりませんが、趣味グラマにとってはコードが動く事がまずは大事という事で、ひとまず解決して良かったです(^^)
Flutter の記事
コメントの受け付けは終了しました。ご意見・ご質問は X(@nm_aru)へどうぞ。


