Flutter App Performance Issues with Long List

Viewed 1616

I have a flutter app where user can add items to a list which are stored in firebase. User can add up to 1000 items at once. Initially this is no issue but with a growing number of list items the app gets slower and slower until when adding multiple items at once after roughly 1000 items are in the list it crashes the app due to the memory use -

thread #10, name = 'io.flutter.1.ui', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0)

How can I improve the code so the performance improves. I would like to keep the setup with the Stream since it lets me dynamically filter the list on the fly. One information here as well is that WidgetA and WidgetB also both use the Stream Data to display the number of list items in the list.

Here is my code a bit simplified for ease of reading:

Main Screen Class:

Widget content(context) {
double h = MediaQuery.of(context).size.height; //screen height
double w = MediaQuery.of(context).size.width; //screen width



return StreamProvider<List<Activity>>.value(
    catchError: (_, __) => null,
    value: DatabaseService().activities(widget.uid),
    builder: (context, snapshot) {
      return SafeArea(
        child: Container(
          //color: Theme.of(context).backgroundColor, //SkyHookTheme.background,
          child: Scaffold(
            backgroundColor: Colors.transparent,
            body: NotificationListener<ScrollNotification>(
              onNotification: _handleScrollNotification,
              child: Stack(children: [
                ListView(
                  controller: _scrollController,
                  children: <Widget>[
                    Column(
                      children: <Widget>[
                   
                        WidgetA(),
                        WidgetB(),
                        ActivityList(),    //List of User Activities

                      ],
                    )
                  ],
                ),
              
              ]),
            ),
          ),
        ),
      );
    });
}

ActivityList Class Listview Building:

ListView buildList(List<Activity> acts){
items = ListView.builder(
  shrinkWrap: true,
  physics: ClampingScrollPhysics(),
  scrollDirection: Axis.vertical,
  itemCount: len,
  itemBuilder: (context, index) {
    return ActivityTile(activity: acts[index], number: acts.length - (index));
  },
);


return items;

}

Any Tips / Hints how I can improve this would be highly appreciated.

Thanks!

4 Answers

You have to pagination to achieve smooth perform And just load 10 documents in one time and with Help of scrollcontroller check you are end of the list And then load next 10 documents that’s would be Efficient manner .

Instead of "listview" use sliversList widget. See the Example of sliversList and sliverscomponents here

I think @AmitSingh's suggestion is best but if you want to load data in once then you can get data in pagination but not when the user scrolls but when you got the first bunch of data.

yeah you should use pagination or lazy-loading! reading and rendering 1000 document at once is too much work for most mobile devices. instead you should load you documents likes this

import 'package:cloud_firestore/cloud_firestore.dart';  
Firestore firestore = Firestore.instance

class LongList extends StatefulWidget {

@override _LongListState createState() => _LongListState(); }

class _LongListState extends State<LongList> {

List<DocumentSnapshot> products = []; // stores fetched products  
bool isLoading = false; // track if products fetching  
bool hasMore = true; // flag for more products available or not  
int documentLimit = 10; // documents to be fetched per request  
DocumentSnapshot lastDocument; // flag for last document from where next   10 records to be fetched  
ScrollController _scrollController = ScrollController(); // listener for    listview scrolling

getProducts() async {  
       if (!hasMore) {  
        print('No More Products');  
        return;  
       }  
   if (isLoading) {  
     return;  
   }  
   setState(() {  
     isLoading = true;  
   });  
 QuerySnapshot querySnapshot;  
   if (lastDocument == null) {  
     querySnapshot = await firestore  
         .collection('products')  
         .orderBy('name')  
         .limit(documentLimit)  
         .getDocuments();  
   } else {  
     querySnapshot = await firestore  
         .collection('products')  
         .orderBy('name')  
         .startAfterDocument(lastDocument)  
         .limit(documentLimit)  
         .getDocuments();  
     print(1);  
   }  
   if (querySnapshot.documents.length < documentLimit) {  
     hasMore = false;  
   }  
   lastDocument = querySnapshot.documents[querySnapshot.documents.length - 1];  
   products.addAll(querySnapshot.documents);  
   setState(() {  
     isLoading = false;  
   });  
   }  

  void initState(){
  getProducts();
  _scrollController.addListener(() {
  double maxScroll = _scrollController.position.maxScrollExtent;
  double currentScroll = _scrollController.position.pixels;
  double delta = MediaQuery.of(context).size.height * 0.20;
  if (maxScroll - currentScroll <= delta) {
    getProducts();
  }
  });
  _pageManager = PageManager();

  super.initState();
  }  
  Widget build(BuildContext context) {  
   return Scaffold(  
     appBar: AppBar(  
       title: Text('Flutter Pagination with Firestore'),  
     ),  
     body: Column(children: [  
       Expanded(  
         child: products.length == 0  
             ? Center(  
                 child: Text('No Data...'),  
               )  
             : ListView.builder(  
                 controller: _scrollController,  
                 itemCount: products.length,  
                 itemBuilder: (context, index) {  
                   return ListTile(  
                     contentPadding: EdgeInsets.all(5),  
                     title: Text(products[index]['name']),  
                     subtitle: Text(products[index]  ['short_desc']),  
                   );  
                 },  
               ),  
       ),  
       isLoading  
           ? Container(  
               width: MediaQuery.of(context).size.width,  
               padding: EdgeInsets.all(5),  
               color: Colors.yellowAccent,  
               child: Text(  
                 'Loading',  
                 textAlign: TextAlign.center,  
                 style: TextStyle(  
                   fontWeight: FontWeight.bold,  
                 ),  
               ),  
             )  
           : Container()  
     ]),  
   );  
 }  

}

Related