How to Create Lazy Loading ListView In Flutter

How to Create Lazy Loading ListView In Flutter?

ListView Widget is one of the important widget types that can be used anywhere. So in this article, we will go through how to create Lazy Loading ListView in flutter.

In this blog post, we’ll explore the concept of lazy loading in Flutter and guide you through the process of creating a lazy loading ListView. By implementing lazy loading, you can significantly improve the performance and efficiency of your app, which is crucial in mobile app development for startups especially when dealing with large data sets or network requests. We’ll cover various techniques and strategies for implementing lazy loading, including using Flutter’s ListView.builder, implementing pagination, and incorporating libraries like Flutter Infinite Scroll. You’ll learn how to dynamically load data as the user scrolls, ensuring a smooth and seamless user experience.

How to Create Lazy Loading ListView In Flutter?

You can listen to a ScrollController. ScrollController has some useful information, such as the scrolloffset and a list of ScrollPosition.

In your case, the interesting part is in the controller.position which is the currently visible ScrollPosition. which represents a segment of the scrollable.

ScrollPosition contains pieces of information about its position inside the scrollable. Such as extentBefore and extentAfter. Or it’s size, with extentInside.

Considering this, you could trigger a server call based on extentAfter which represents the remaining scroll space available.

A basic example will have a code snippet like the below:

class MyHome extends StatefulWidget {
  @override
  _MyHomeState createState() => new _MyHomeState();
}

class _MyHomeState extends State<MyHome> {
  ScrollController controller;
  List<String> items = new List.generate(100, (index) => 'Hello $index');

  @override
  void initState() {
    super.initState();
    controller = new ScrollController()..addListener(_scrollListener);
  }

  @override
  void dispose() {
    controller.removeListener(_scrollListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Scrollbar(
        child: new ListView.builder(
          controller: controller,
          itemBuilder: (context, index) {
            return new Text(items[index]);
          },
          itemCount: items.length,
        ),
      ),
    );
  }

  void _scrollListener() {
    print(controller.position.extentAfter);
    if (controller.position.extentAfter < 500) {
      setState(() {
        items.addAll(new List.generate(42, (index) => 'Inserted $index'));
      });
    }
  }
}

You can clearly see that when reaching the end of the scroll, it scrollbar expends due to having loaded more items.

You can also try the below solution:

bool _handleScrollNotification(ScrollNotification notification) {
  if (notification is ScrollEndNotification) {
    if (_controller.position.extentAfter == 0) {
      loadMore();
    }
  }
  return false;
}

@override
Widget build(BuildContext context) {
    final Widget gridWithScrollNotification = NotificationListener<
            ScrollNotification>(
        onNotification: _handleScrollNotification,
        child: GridView.count(
            controller: _controller,
            padding: EdgeInsets.all(4.0),
          // Create a grid with 2 columns. If you change the scrollDirection to
          // horizontal, this would produce 2 rows.
          crossAxisCount: 2,
          crossAxisSpacing: 2.0,
          mainAxisSpacing: 2.0,
          // Generate 100 Widgets that display their index in the List
          children: _documents.map((doc) {
            return GridPhotoItem(
              doc: doc,
            );
          }).toList()));
    return new Scaffold(
      key: _scaffoldKey,
      body: RefreshIndicator(
       onRefresh: _handleRefresh, child: gridWithScrollNotification));
}

Basically, when ListView builds the last item and that means the user has scrolled down to the bottom.

For purpose of the demo, I have changed the example to let a page only include one item and add a CircularProgressIndicator.

...
bool _loadingMore;
bool _hasMoreItems;
int  _maxItems = 30;
int  _numItemsPage = 1;
...
_hasMoreItems = items.length < _maxItems;    
...
return IncrementallyLoadingListView(
              hasMore: () => _hasMoreItems,
              itemCount: () => items.length,
              loadMore: () async {
                // can shorten to "loadMore: _loadMoreItems" but this syntax is used to demonstrate that
                // functions with parameters can also be invoked if needed
                await _loadMoreItems();
              },
              onLoadMore: () {
                setState(() {
                  _loadingMore = true;
                });
              },
              onLoadMoreFinished: () {
                setState(() {
                  _loadingMore = false;
                });
              },
              loadMoreOffsetFromBottom: 0,
              itemBuilder: (context, index) {
                final item = items[index];
                if ((_loadingMore ?? false) && index == items.length - 1) {
                  return Column(
                    children: <Widget>[
                      ItemCard(item: item),
                      Card(
                        child: Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: Column(
                            children: <Widget>[
                              Row(
                                crossAxisAlignment:
                                    CrossAxisAlignment.start,
                                children: <Widget>[
                                  Container(
                                    width: 60.0,
                                    height: 60.0,
                                    color: Colors.grey,
                                  ),
                                  Padding(
                                    padding: const EdgeInsets.fromLTRB(
                                        8.0, 0.0, 0.0, 0.0),
                                    child: Container(
                                      color: Colors.grey,
                                      child: Text(
                                        item.name,
                                        style: TextStyle(
                                            color: Colors.transparent),
                                      ),
                                    ),
                                  )
                                ],
                              ),
                              Padding(
                                padding: const EdgeInsets.fromLTRB(
                                    0.0, 8.0, 0.0, 0.0),
                                child: Container(
                                  color: Colors.grey,
                                  child: Text(
                                    item.message,
                                    style: TextStyle(
                                        color: Colors.transparent),
                                  ),
                                ),
                              )
                            ],
                          ),
                        ),
                      ),
                      Center(child: CircularProgressIndicator())
                    ],
                  );
                }
                return ItemCard(item: item);
              },
            );

We will get output like a below:

Lazy ListView
Lazy ListView

You can also use plugin lazy_load_scrollview

So you can also try the below approach:

NotificationListener(onNotification: _onScrollNotification, child: GridView.builder())

bool _onScrollNotification(ScrollNotification notification) {
    if (notification is ScrollEndNotification) {
      final before = notification.metrics.extentBefore;
      final max = notification.metrics.maxScrollExtent;

      if (before == max) {
        // load next page
        // code here will be called only if scrolled to the very bottom
      }
    }
    return false;
  }

Conclusion:

Thanks for reading and we hope you enjoyed this article !!!

So in this article, we have been through how to create Lazy Loading ListView in the flutter.

Lots of amazing content coming your way…..flutter infinite scroll, flutter lazy load widget, flutter listview pagination, flutter listview load more on a scroll, listview.builder flutter, etc.

Do not forget to drop your valuable suggestions/feedback. We would love to assist you.

Abhishek Dhanani

Written by Abhishek Dhanani

Abhishek Dhanani, a skilled software developer with 3+ years of experience, masters Dart, JavaScript, TypeScript, and frameworks like Flutter and NodeJS. Proficient in MySQL, Firebase, and cloud platforms AWS and GCP, he delivers innovative digital solutions.

Leave a comment

Your email address will not be published. Required fields are marked *


Discuss Your Project

Connect with Flutter Agency's proficient skilled team for your app development projects across different technologies. We'd love to hear from you! Fill out the form below to discuss your project.

Have Project For Us

Get in Touch

"*" indicates required fields

ready to get started?

Fill out the form below and we will be in touch soon!

"*" indicates required fields