'How to use RefreshIndicator to update FutureBuilder state?
I am using FutureBuilder to show the data loaded from server. I want to show the loading state only once when the app starts, that is why I am calling the API from initState. The data I get from server may change and to reflect the change in UI, I am using refreshIndicator. The problem is that I could not come up with a solution to update the state.
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
Future<List<Photo>> _photosServer;
@override
void initState() {
super.initState();
_photosServer = ApiRest.getPhotos();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: () {
_refreshIndicatorKey.currentState.show();
await getPhotosFromServer();
...
},
child: FutureBuilder(
future: _photosServer,
builder: (BuildContext context, snapshot) {
if (snapshot.data == null) {
return Center(
child: Text('Loading...'),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, index) => ListTile(
title: Text(snapshot.data[index].title),
),
);
},
),
),
);
}
}
In the onRefresh function, I am using the following code to show the RefreshIndicator while getting data from server.
onRefresh: () {
_refreshIndicatorKey.currentState.show();
await getPhotosFromServer();
...
}
What else should I do to handle the issue?
Solution 1:[1]
You can have a separate List<Photo>
variable which can be updated by the FutureBuilder or the RefreshIndicator, and do something like this:
class _MyHomePageState extends State<MyHomePage> {
GlobalKey<RefreshIndicatorState> _refreshIndicatorKey =
GlobalKey<RefreshIndicatorState>();
List<Photo> _photosList;
Future<void> _initPhotosData;
@override
void initState() {
super.initState();
_initPhotosData = _initPhotos();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: FutureBuilder(
future: _initPhotosData,
builder: (BuildContext context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
{
return Center(
child: Text('Loading...'),
);
}
case ConnectionState.done:
{
return RefreshIndicator(
key: _refreshIndicatorKey,
onRefresh: _refreshPhotos,
child: ListView.builder(
itemCount: _photosList.length,
itemBuilder: (BuildContext context, index) => ListTile(
title: Text(_photosList[index].title),
),
));
}
}
},
),
);
}
Future<void> _initPhotos() async {
final photos = await ApiRest.getPhotos();
_photosList = photos;
}
Future<void> _refreshPhotos() async {
final photos = await ApiRest.getPhotos();
setState(() {
_photosList = photos;
});
}
}
Solution 2:[2]
onRefresh: () {
return Future(() { setState(() {}); });
},
In your RefreshIndicator will refresh child FutureBuilder
Solution 3:[3]
In order to refresh data and show the refresh indicator, simply wait for the results and afterwards update the future:
onRefresh: () async {
final results = await getPhotosFromServer();
setState(() {
_photosServer = Future.value( results );
});
},
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Alfred Jingle |
Solution 2 | Danilyer |
Solution 3 | moodstubos |