ListView builder Flutter API

Flutter: ListView Pagination [Load More] example

Last updated on August 2, 2021 A Goodman Loading... Loading... 2 comments

Flutter

[ 318 Articles]

How to create a Filter/Search ListView in Flutter [2022]

January 4, 2022

Flutter: ListTile examples

July 29, 2021

Sorting Lists in Dart and Flutter [5 Examples]

May 21, 2021

Customize Borders of TextField/TextFormField in Flutter

April 1, 2021

Most Popular Packages for State Management in Flutter [2022]

January 1, 2022

Working with ElevatedButton in Flutter [2022]

January 4, 2022

Flutter: Add a Search Field to an App Bar [2 Approaches]

August 10, 2021

Flutter StreamBuilder examples [null safety]

January 3, 2022

More

report this ad

In real Flutter applications, we often load dynamic data from servers or databases instead of using hardcode/dummy data as we often see in the examples.

For example, you have an e-commerce app and need to load products from Rest API from the server-side. If we only have 10 or 20 products, it will be easy to fetch them all with just one get request. However, if your platform has thousands of products or even millions of products, you have to implement pagination to reduce the burden on the server and reduce latency and improve the apps performance as well.Advertisements

This article walks you through a complete example of creating a ListView with pagination in Flutter.

Table of Contents

  • The Strategy
  • App Preview
  • The Code
  • Conclusion

The Strategy

In the beginning, we send a get request and load only a fixed number of results [choose a number that makes sense in your case] and display them in a ListView.

When the user scrolls to the bottom of the ListView, we send another GET request and fetch another fixed number of results, and so on.

AdvertisementsTo know when the user scrolls near the bottom of a ListView, we will create a ScrollController and attach it to the ListView. When ScrollController.position.extendAfter has a value less than a certain level [200 or 300 is good to go], we will call a function that sends a new GET request.

You can find more information about ScrollController in the official docs. If you feel the words are too boring and confusing, just jump straight to the practical example below.

App Preview

This example will load some dummy blog posts from a public Pest API endpoint provided by jsonplaceholder.typicode.com [special thanks to the author for making an awesome tool for testing purposes]:

//jsonplaceholder.typicode.com/posts?_page=[page-number]&_limit=[some-number]
Advertisements

When the app launches for the first time, we will fetch the first 20 posts. Next, every time we scroll near the bottom of the ListView, a function named _loadMore will be called and this function will load 20 more posts.

After all the posts from the API have been loaded, a message will appear letting us know that even if we scroll down, nothing will happen.

Progress indicators will also appear while we are sending requests to the server.

A demo is worth more than thousands of words:

The Code

1. Create a new Flutter project.

Advertisements

2. To send http requests with ease, we will the http package. Install it by running:

dart pub add http

3. Remove all the default code in your main.dart and add the following:

// main.dart import 'package:flutter/material.dart'; import 'dart:convert'; import 'package:http/http.dart' as http; void main[] { runApp[MyApp[]]; } class MyApp extends StatelessWidget { @override Widget build[BuildContext context] { return MaterialApp[ // Remove the debug banner debugShowCheckedModeBanner: false, title: 'Kindacode.com', theme: ThemeData[ primarySwatch: Colors.pink, ], home: HomePage[]]; } } class HomePage extends StatefulWidget { const HomePage[{Key? key}] : super[key: key]; @override _HomePageState createState[] => _HomePageState[]; } class _HomePageState extends State { // We will fetch data from this Rest api final _baseUrl = '//jsonplaceholder.typicode.com/posts'; // At the beginning, we fetch the first 20 posts int _page = 0; int _limit = 20; // There is next page or not bool _hasNextPage = true; // Used to display loading indicators when _firstLoad function is running bool _isFirstLoadRunning = false; // Used to display loading indicators when _loadMore function is running bool _isLoadMoreRunning = false; // This holds the posts fetched from the server List _posts = []; // This function will be called when the app launches [see the initState function] void _firstLoad[] async { setState[[] { _isFirstLoadRunning = true; }]; try { final res = await http.get[Uri.parse["$_baseUrl?_page=$_page&_limit=$_limit"]]; setState[[] { _posts = json.decode[res.body]; }]; } catch [err] { print['Something went wrong']; } setState[[] { _isFirstLoadRunning = false; }]; } // This function will be triggered whenver the user scroll // to near the bottom of the list view void _loadMore[] async { if [_hasNextPage == true && _isFirstLoadRunning == false && _isLoadMoreRunning == false && _controller.position.extentAfter < 300] { setState[[] { _isLoadMoreRunning = true; // Display a progress indicator at the bottom }]; _page += 1; // Increase _page by 1 try { final res = await http.get[Uri.parse["$_baseUrl?_page=$_page&_limit=$_limit"]]; final List fetchedPosts = json.decode[res.body]; if [fetchedPosts.length > 0] { setState[[] { _posts.addAll[fetchedPosts]; }]; } else { // This means there is no more data // and therefore, we will not send another GET request setState[[] { _hasNextPage = false; }]; } } catch [err] { print['Something went wrong!']; } setState[[] { _isLoadMoreRunning = false; }]; } } // The controller for the ListView late ScrollController _controller; @override void initState[] { super.initState[]; _firstLoad[]; _controller = new ScrollController[]..addListener[_loadMore]; } @override void dispose[] { _controller.removeListener[_loadMore]; super.dispose[]; } @override Widget build[BuildContext context] { return Scaffold[ appBar: AppBar[ title: Text['Kindacode.com'], ], body: _isFirstLoadRunning ? Center[ child: CircularProgressIndicator[], ] : Column[ children: [ Expanded[ child: ListView.builder[ controller: _controller, itemCount: _posts.length, itemBuilder: [_, index] => Card[ margin: EdgeInsets.symmetric[vertical: 8, horizontal: 10], child: ListTile[ title: Text[_posts[index]['title']], subtitle: Text[_posts[index]['body']], ], ], ], ], // when the _loadMore function is running if [_isLoadMoreRunning == true] Padding[ padding: const EdgeInsets.only[top: 10, bottom: 40], child: Center[ child: CircularProgressIndicator[], ], ], // When nothing else to load if [_hasNextPage == false] Container[ padding: const EdgeInsets.only[top: 30, bottom: 40], color: Colors.amber, child: Center[ child: Text['You have fetched all of the content'], ], ], ], ], ]; } }

Note: In many cases, the server-side gives us information about the total number of results it has or the total number of pages it has. In these scenarios, you can still do the same as the example above or modify the code a bit to check whether _loadMore should be called or not.

Conclusion

Weve examined an end-to-end example of implementing a ListView with pagination in a Flutter app. When you use your own API, there may be some differences in the URL structure, for example:

//www.example.com/api/products?currentPage=1&perPage=10 //www.kindacode.com/api/users?start=10&limit=20&order=desc

Make sure you understand the API youre going to get data from. If not, talk to your backend developers and ask them to provide the necessary information.

Advertisements

If youd like to learn more new stuff about Flutter, take a look at the following articles:

  • Flutter & SQLite: CRUD Example
  • Flutter and Firestore Database: CRUD example [null safety]
  • Using GetX [Get] for Navigation and Routing in Flutter
  • Using GetX [Get] for State Management in Flutter
  • Flutter FutureBuilder example [null safety]
  • Flutter StreamBuilder examples [null safety]

You can also take a tour around our Flutter topic page, or Dart topic page for the latest tutorials and examples.

Advertisements
Share Tweet Telegram
Subscribe
Notify of
I allow to use my email address and send notification about new comments and replies [you can unsubscribe at any time].
Label
{} [+]
Name*
Email*
Label
{} [+]
Name*
Email*
2 Comments
Inline Feedbacks
View all comments
nairanitu
3 months ago

how to handle this using provider?

0
Reply
Author
A Goodman
3 months ago
Reply to nairanitu

There isnt much difference. Maybe I will write a new & standalone article about using provider.

0
Reply

Related Articles

Flutter: Creating Custom Back Buttons

December 28, 2021

Flutter: Creating an Auto-Resize TextField

December 28, 2021

Flutter: Columns with Percentage Widths

December 28, 2021

Flutter & Hive Database: CRUD Example [2022]

January 3, 2022

Flutter: Text with Read More / Read Less Buttons

December 24, 2021

Flutter: Making a Dropdown Multiselect with Checkboxes

December 24, 2021

Using Stepper widget in Flutter: Tutorial & Example

December 23, 2021

Using Provider for State Management in Flutter [2022]

December 25, 2021

Using BlockSemantics in Flutter: Tutorial & Example

December 21, 2021

Flutter: AbsorbPointer example

December 20, 2021

Flutter: TextField and Negative Numbers

December 18, 2021

Flutter: Creating OTP/PIN Input Fields [2 approaches]

December 18, 2021

Video liên quan

Chủ Đề