Hi, guys I hope you all are well so in this article we learn to crate RefreshIndicator, scrolling pagination with the dynamic data from the server. First, we create a simple scrolling pagination with static values then we go further with dynamic data.
A simple workaround for the infinite scroll (Pagination) in Flutter ListView!
We face many requirements in our daily application development for infinite scrolling (pagination)in ListView and we are like. We start creating ListView then create a list of widgets and set scroll listener and set error or failure scenarios and then lastly, stop the pagination when data loading is completed.
What if I tell you that you have one widget in which you just need to pass your callback with data that will be called when the user reaches the end of the list?
Step 1: Implement pagination from scratch
Open main.dart file and implement these codes in it. normally this code navigates into separate classes where we write and execute the scrolling pagination code.
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'Scrolling Pagination/scrolling_pagination.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky, overlays:[]).then( (_) => runApp(MyApp()), ); // runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, home: ScrollingPagination(), ); } }
Step 2: Create and Open ScrollingPagination.dart file
First you create A-List of 15 items that we display inside ListView.builder.
List<String> items = List.generate(15, ((index) => 'Item ${index+1}') );
once you reach the bottom of the list then we load more data so simply one more item to list view itemCount: items.length + 1 .
ListView.builder( controller: controller, padding: EdgeInsets.all(8.0), itemCount: items.length+1, itemBuilder: (context, index) { final item = items[index]; return ListTile(title: Text(item),); }),
then apply the if else condition when the data not the last so the simple list other vise show the else condition inside the else condition we put CircularProgressIndicator.
ListView.builder( padding: EdgeInsets.all(8.0), itemCount: items.length+1, itemBuilder: (context, index) { if(index < items.length){ final item = items[index]; return ListTile(title: Text(item),); }else{ return Padding(padding: EdgeInsets.all(32), child: Center(child: CircularProgressIndicator(),), ); } }),
next we create ScrollController and add into ListView.builder. and initState we listen to the scrollcontroller and check if they have reach the end of the list then we fetch more data. and also we dispose the ScrollController.
final controller = ScrollController(); @override void initState() { super.initState(); controller.addListener(() { if(controller.position.maxScrollExtent == controller.offset){ fetch(); } }); } Future fetch() async{ setState(() { items.addAll(['Item A','Item B','Item C','Item D']); }); }
Full Code:
import 'package:flutter/material.dart'; class ScrollingPagination extends StatefulWidget { const ScrollingPagination({Key? key}) : super(key: key); @override State<ScrollingPagination> createState() => _ScrollingPaginationState(); } class _ScrollingPaginationState extends State<ScrollingPagination> { List<String> items = List.generate(15, ((index) => 'Item ${index + 1}')); final controller = ScrollController(); @override void initState() { super.initState(); controller.addListener(() { if (controller.position.maxScrollExtent == controller.offset) { fetch(); } }); } Future fetch() async { setState(() { items.addAll(['Item A', 'Item B', 'Item C', 'Item D']); }); } @override void dispose() { // TODO: implement dispose super.dispose(); controller.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Scrolling Pagination'), centerTitle: true, ), body: ListView.builder( controller: controller, padding: EdgeInsets.all(8.0), itemCount: items.length + 1, itemBuilder: (context, index) { if (index < items.length) { final item = items[index]; return ListTile( title: Text(item), ); } else { return Padding( padding: EdgeInsets.all(32), child: Center( child: CircularProgressIndicator(), ), ); } }), ); } }
Scrolling Pagination with Dynamic data(API)
it’s very simple like previews one but add some more code like this you use HTTP package(http: ^0.13.5) to load some item from the server when the response code success then we get response body into newItems which add to the current item list
dependencies: http: ^0.13.5
Future fetch() async{ final url = Uri.parse('https://jsonplaceholder.typicode.com/posts'); final responce = await http.get(url); if(responce.statusCode == 200){ final List newItems = jsonDecode(responce.body); setState(() { page++; isLoading = false; if(newItems.length < limit){ hasMore = false; } items.addAll(newItems.map<String>((item){ final number = item['id']; return 'Item $number'; }).toList()); }); }
let go to the website from this URL we get the data in from of Json in this time we only use the id
ListView.builder( controller: controller, padding: EdgeInsets.all(8.0), itemCount: items.length + 1, itemBuilder: (context, index) { if(index < items.length){ final item = items[index]; return ListTile( title: Text(item), ); } else{ return Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center( child: hasMore ? CircularProgressIndicator() : Text("No more data"), ), ); } }),
therefore we map over the item from each item extract the id and return text item which number. nwext you set the limit like page 1 define the first 25 data and if page 2 is define other 25 data and so on
final url = Uri.parse('https://jsonplaceholder.typicode.com/posts?_limit=$limit&_page=$page');
and finally wrap around the ListView into RefreshIndicator in with the refresh method
RefreshIndicator( onRefresh: refresh, child: ListView.builder( controller: controller, padding: EdgeInsets.all(8.0), itemCount: items.length + 1, itemBuilder: (context, index) { if(index < items.length){ final item = items[index]; return ListTile( title: Text(item), ); } else{ return Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center( child: hasMore ? CircularProgressIndicator() : Text("No more data"), ), ); } }), )
Full Code:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; class ScrollingPagination extends StatefulWidget { const ScrollingPagination({Key? key}) : super(key: key); @override State<ScrollingPagination> createState() => _ScrollingPaginationState(); } class _ScrollingPaginationState extends State<ScrollingPagination> { final controller = ScrollController(); List<String> items = []; bool hasMore = true; int page = 1; bool isLoading = false; @override void initState() { super.initState(); fetch(); controller.addListener(() { if (controller.position.maxScrollExtent == controller.offset) { fetch(); } }); } @override void dispose() { controller.dispose(); super.dispose(); } Future fetch() async { if (isLoading) return; isLoading = true; const limit = 25; final url = Uri.parse( 'https://jsonplaceholder.typicode.com/posts?_limit=$limit&_page=$page'); final responce = await http.get(url); if (responce.statusCode == 200) { final List newItems = jsonDecode(responce.body); setState(() { page++; isLoading = false; if (newItems.length < limit) { hasMore = false; } items.addAll(newItems.map<String>((item) { final number = item['id']; return 'Item $number'; }).toList()); }); } } Future refresh() async { setState(() { isLoading = false; hasMore = true; page = 0; items.clear(); }); fetch(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Scrolling Pagination'), centerTitle: true, ), body: RefreshIndicator( onRefresh: refresh, child: ListView.builder( controller: controller, padding: EdgeInsets.all(8.0), itemCount: items.length + 1, itemBuilder: (context, index) { if (index < items.length) { final item = items[index]; return ListTile( title: Text(item), ); } else { return Padding( padding: EdgeInsets.symmetric(vertical: 32), child: Center( child: hasMore ? CircularProgressIndicator() : Text("No more data"), ), ); } }), )); } }
Conclusion :
For the major requirement of Flutter pagination ListView, Flutter Pagination Helper will be a more suitable option and for more specification, you can go with the first option.
And thanks for reading this blog, for detail info please click here
For more blogs please click here.
So pls follow the above step and if you have any issues or suggestions you can leave your message in the comment section I will try to solve this.