Skip to content

Instantly share code, notes, and snippets.

@tombailey
Created June 3, 2020 15:07
Show Gist options
  • Save tombailey/988f788493cec9b95e7e9e007b8a7a0d to your computer and use it in GitHub Desktop.
Save tombailey/988f788493cec9b95e7e9e007b8a7a0d to your computer and use it in GitHub Desktop.
Flutter - Infinite scrolling / loading list view
import 'package:flutter/material.dart';
extension on List {
Object lastOrNull() {
return this.isNotEmpty ? this.last : null;
}
}
typedef ItemWidgetBuilder = Widget Function(Object item);
typedef FutureItemsCallback = Future<List<Object>> Function(Object lastLoadedItem);
typedef ItemCallback = void Function(Object item);
class InfiniteList extends StatefulWidget {
final ItemWidgetBuilder widgetBuilder;
final FutureItemsCallback loadMore;
final ItemCallback onItemSelected;
InfiniteList({Key key, @required this.widgetBuilder, @required this.loadMore, this.onItemSelected}) : super(key: key);
@override
State<StatefulWidget> createState() {
return InfiniteListState();
}
}
class InfiniteListState extends State<InfiniteList> {
List<Object> items = [];
bool shouldTryToLoadMore = true;
@override
void initState() {
super.initState();
waitOnItems();
}
void waitOnItems() async {
try {
final items = await widget.loadMore(this.items.lastOrNull());
this.shouldTryToLoadMore = items.isNotEmpty;
setState(() {
this.items.addAll(items);
});
} catch(error) {
print(error);
}
}
@override
Widget build(BuildContext context) {
if (items.isEmpty) {
return initiallyLoading();
} else {
//TODO: show progress bar at the bottom if loading more
return list();
}
}
Widget list() {
return ListView.builder(
itemCount: shouldTryToLoadMore ? null : items.length,
itemBuilder: (context, index) {
if (shouldTryToLoadMore && index == items.length - 1) {
waitOnItems();
return null;
} else if (index >= items.length) {
return null;
} else if (widget.onItemSelected != null) {
return InkWell(
onTap: () => {
widget.onItemSelected(items[index])
},
child: widget.widgetBuilder(items[index]),
);
} else {
return widget.widgetBuilder(items[index]);
}
}
);
}
Widget initiallyLoading() {
return Center(
child: CircularProgressIndicator(),
);
}
}
import 'package:flutter/material.dart';
class Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return InfiniteList(
widgetBuilder: (item) {
return Text(item);
},
loadMore: (lastLoaded) {
if (lastLoaded == null) {
//first load request
return ["hello", "world"];
} else {
//subsequent load request(s)
return [];
}
},
onItemSelected: (item) {
print(item);
},
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment