I am currently developing a Flutter App which uses a lots of REST APIs to communicate with server side. I am facing some performance issues related to FutureBuilder widget implemented as below:
RefreshIndicator(
displacement: 100.0,
onRefresh: _refreshLastEntries,
child: FutureBuilder<List<Entry>> (
future: lastEntriesList,
builder: (context, snapshot) {
if(snapshot.hasError) print(snapshot.error);
if(snapshot.data == null) return Container();
else return ListView.builder(
controller: _controller,
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: EntryCard(entry: snapshot.data[index])
);
},
);
},
),
),
By the way, there are 2 more FutureBuilder implementations with RefreshIndicator in the same page build, and they are wrapped with a PageView. The Flutter Performance tab in Android Studio showing avg. fps of 30 frames/second (sometimes < 30) in debug mode. It is better in release mode but still below 60 fps that I can notice a laggy animation.
Well I know the problem (not sure though) is FutureBuilder trying to rebuild as long as the AsyncSnapshot
has data. But I couldn't figure out a better implementation of this. The refresh function _refreshLastEntries()
is implemented as:
Future<Null> _refreshLastEntries() async {
setState(() {
lastEntriesList = fetchLastEntries(2);
});
return Future.delayed(Duration(milliseconds: 600));
}
And the fetchLastEntries(int)
is just for the getting the data and map it into some EntryModel class. I have tried a setting a global value after successfully getting the data and then use it for building the ListView but it did not work.
I am testing this app with a Xiaomi MI6 with Snapdragon 835 and Adreno 540 if that matters.
I will not attach any other code as the whole PageView build code since it is hard to reproduce the same build, thanks!
The FutureBuilder widget is not supposed to rebuild if the future status does not change. What probably happens in your case is that
setState(() {
lastEntriesList = fetchLastEntries(2);
});
inside _refreshLastEntries()
causes a rebuild with the uncompleted future which, upon completion, causes another rebuild through the FutureBuilder.
Since you're using a RefreshIndicator, my opinion is that you don't need a FutureBuilder. Do the asynchronous work inside _refreshLastEntries()
and when complete, update a ListView.builder widget. To be clearer:
//Declare lastEntriesList as a list
List<Entry> lastEntriesList;
Future<void> _refreshLastEntries() async {
return fetchLastEntries(2).then((lastEntries) {
setState(() {
lastEntriesList = lastEntries;
});
});
}
And modify your subtree as follows:
RefreshIndicator(
displacement: 100.0,
onRefresh: _refreshLastEntries,
child: ListView.builder(
controller: _controller,
itemCount: lastEntriesList.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(10.0),
child: EntryCard(entry: lastEntrieslist[index])
);
},
);,
)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.