I have a ListView builder in my Flutter app that does some heavy computation for each item in the list, so it can take some time to populate new elements. The computation is all synchronous code. I’d like to add a spinner while the list is adding new elements, but don’t want to change all the computation code. So I think that means making the itemBuilder itself async, but I’m not sure how to do this. Is it possible?
Below is the code I had, trying to use the EasyLoading package, but that didn’t work, because the APIs from EasyLoading are async, which wasn’t mentioned in the description.
class Matches extends StatefulWidget {
final Iterator<String> _match;
Matches(this._matcher);
@override
MatchesState createState() => MatchesState(_match);
}
class MatchesState extends State<Matches> {
final Iterator<String> _matcher;
final _matches = <String>[];
MatchesState(this._matcher);
_next() {
// This is async so doesn't work
EasyLoading.show(status: 'Searching...');
// Get the next ten results
for (var i = 0; i < 10; i++) {
// _matcher is a synchronous iterator that does some
// computationally intensive work each iteration.
if (_matcher.moveNext()) {
_matches.add(_matcher.current);
EasyLoading.showProgress(0.1 * i, status: 'Searching...');
} else {
EasyLoading.showSuccess('Searching...');
break;
}
}
EasyLoading.dismiss(animation: false);
}
Widget _buildMatches() {
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemBuilder: (context, i) {
if (i.isOdd) return Divider();
final index = i ~/ 2;
if (index >= _matches.length) {
_next();
}
final row = (index < _matches.length) ? _matches[index] : '';
return ListTile(title: Text(row));
}
);
}
@override
Widget build(BuildContext context) {
return FlutterEasyLoading(
child: Scaffold(
appBar: AppBar(
title: Text(TITLE),
),
body: _buildMatches(),
));
}
}
It seems like I need to use a FutureBuilder. I found a good article here https://blog.devgenius.io/understanding-futurebuilder-in-flutter-491501526373 but it (like most I have seen) assumes that all the results are computed before the list is populated, whereas I want to build my list lazily as the user scrolls through it. So I need something like FutureListItemBuilder, if that only existed.
Update: I found this article which is closer to what I want to do; going to see if I can make it work for me: https://medium.com/theotherdev-s/getting-to-know-flutter-list-lazy-loading-1cb0ed5de91f
2
Answers
I believe I have figured it out. The trick was to use Future.delayed on each update, and as Muhammad Qazmouz mentioned, just return the spinner if I hadn't retrieved a requested item yet. I might need to refine this a bit, but the code is below:
While your data is a synchronous, so you can fetch about loading in the
ListView.builder
so you can add if statement to your code, like this:And I didn’t see any of
async, await
, and theitemCount
in your code!!!