Saturday, November 3, 2018

Flutter - Infinite scroll or load more list.

Infinite scroll or load more list is the popular replacement of pagination in a mobile application. With the help of pagination, you can display big data in a small piece of data.

Flutter load more list itemPagination is a common feature in heavy content apps. It breaks down a list of content into equal smaller pieces and loaded one at a time. Such as, in Facebook, Twitter and Instagram, we spend countless hours scrolling. They just don’t seem to end.

In mobility, if you going to make a app that have a ton of content that takes too long to load. From a developer perspective, how would you load all of that content?. It is not possible to make a call for such huge content at one go. We’ll have to request them in parts or pages.
Pagination allows the user to see the latest content with little wait time. As we load the next page by the time users scroll to the bottom, more content is loaded and available. In mobility, we can implement it with two ways:
  • The first option is to add a button to the footer of the view. When pressed it will load more content.
  • The second option is to check if the last item is visible and load more content.
I really like the second one, because i think its more user friendly.

Let's create a Futter application and implement a load more feature in the list. I'm assuming, you have basice knowladage of Flutter widgets development and you know list view in Flutter. 

We'll create following load more widget:

Flutter load more list item demo


Creating a new Project

1. Create a new project from File ⇒ New Flutter Project with your development IDE.
2. Open main.dart file and edit it. As we have set our theme and change debug banner property of Application.

main.dart
import 'package:flutter/material.dart'; import 'package:flutter_load_more/home.dart'; void main() => runApp(new MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return new MaterialApp( debugShowCheckedModeBanner: false, theme: new ThemeData( primaryColor: const Color(0xFF02BB9F), primaryColorDark: const Color(0xFF167F67), accentColor: const Color(0xFF167F67), ), home: new Home(), ); } }
3. Now create another dart file and give name home.dart. Here, we'll write our user interface widgets and load more logic. We using english_words 3.1.4 to display list content. We have added this in our project dependency. We'll fetch data from this lib when load more event occurs.
4. To track the last item of list, we using ScrollController. We have created a instance of ScrollController and we have set a listener method. It'll check list item and notify us if reach at last item of the list. Afterthat, we calling startLoader method.
  _scrollListener() {                                              home.dart
    if (controller.position.pixels == controller.position.maxScrollExtent) {
      startLoader();
    }
  }
5. StartLoader method will start a progress bar widget and call the fetchData method.
 void startLoader() {                                               home.dart

    setState(() {
      isLoading = !isLoading;
      fetchData();
    });
  }
6. In the fetchData method, we have set 2 second delay. Actually, it behaves like an API call request. After 2 second, we calling the onResponse method. It'll update the list of item with new items and stop loader.
fetchData() async {                                                 home.dart

    var _duration = new Duration(seconds: 2);
    return new Timer(_duration, onResponse);
  }

  void onResponse() {
    setState(() {
      isLoading = !isLoading;
      _All.addAll(generateWordPairs().take(20));
    });
  }


The final home.dart final look like below.
home.dart
import 'dart:async';
import 'package:english_words/english_words.dart'; import 'package:flutter/material.dart'; class Home extends StatefulWidget { @override createState() => new HomeState(); } class HomeState extends State<Home> { ScrollController controller; final _All = <WordPair>[]; final _saved = new Set<WordPair>(); final _biggerFont = const TextStyle(fontSize: 18.0); GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>(); bool isLoading = false; @override void initState() { super.initState(); _All.addAll(generateWordPairs().take(20)); controller = new ScrollController()..addListener(_scrollListener); } @override void dispose() { super.dispose(); controller.dispose(); } void _scrollListener() { if (controller.position.pixels == controller.position.maxScrollExtent) { startLoader(); } } void startLoader() { setState(() { isLoading = !isLoading; fetchData(); }); } fetchData() async { var _duration = new Duration(seconds: 2); return new Timer(_duration, onResponse); } void onResponse() { setState(() { isLoading = !isLoading; _All.addAll(generateWordPairs().take(20)); }); } @override Widget build(BuildContext context) { return new Scaffold( key: scaffoldKey, appBar: new AppBar( title: new Text( "List load more example", style: TextStyle(color: Colors.white), ), ), body: new Stack( children: <Widget>[ _buildSuggestions(), _loader(), ], ), ); } Widget _buildRow(WordPair pair) { final alreadySaved = _saved.contains(pair); return new Column( children: <Widget>[ new ListTile( title: new Text( pair.asPascalCase, style: _biggerFont, ), trailing: new Icon( alreadySaved ? Icons.check : Icons.check, color: alreadySaved ? Colors.deepOrange : null, ), onTap: () { setState(() { if (alreadySaved) { _saved.remove(pair); } else { _saved.add(pair); } }); }, ), new Divider(), ], ); } Widget _buildSuggestions() { return new ListView.builder( padding: const EdgeInsets.all(16.0), // The itemBuilder callback is called once per suggested word pairing, // and places each suggestion into a ListTile row. // For even rows, the function adds a ListTile row for the word pairing. // For odd rows, the function adds a Divider widget to visually // separate the entries. Note that the divider may be difficult // to see on smaller devices. controller: controller, itemCount: _All.length, itemBuilder: (context, i) { // Add a one-pixel-high divider widget before each row in theListView. return _buildRow(_All[i]); }); } Widget _loader() { return isLoading ? new Align( child: new Container( width: 70.0, height: 70.0, child: new Padding( padding: const EdgeInsets.all(5.0), child: new Center(child: new CircularProgressIndicator())), ), alignment: FractionalOffset.bottomCenter, ) : new SizedBox( width: 0.0, height: 0.0, ); } }
If you have followed the article carefully, you can see the app running very smoothly as shown in the above. But if you are facing any problem. If still, you have any quires, please feel free to ask it from below comment section and you can ask it on SLACK.

Share:

Get it on Google Play

React Native - Start Development with Typescript

React Native is a popular framework for building mobile apps for both Android and iOS. It allows developers to write JavaScript code that ca...