Created
June 3, 2020 15:07
-
-
Save tombailey/988f788493cec9b95e7e9e007b8a7a0d to your computer and use it in GitHub Desktop.
Flutter - Infinite scrolling / loading list view
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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