Is there a simple way to load/get more Firebase documents as a user scrolls?

Viewed 50

My app has posts from users and as of now it just fetches all of the posts from Firebase and shows a ListView.builder with all of them. Right now there aren’t too many posts so it’s not an issue but for scalability I want to make just a few posts load in the beginning then load more as the users scroll like Instagram and a lot of other apps do. My idea with this is to save on reads and costs. How can I do this while making sure the most recent posts load first?

1 Answers

you can use limit and startAfter method of firebase to fetch items(data).

  • limit used to limit the number of documents retrieved from the firebase(check the firebase doc here)

  • startAfter or startAt methods to define the start point for a query. (Check the firebase doc here)

         ` class Users extends StatefulWidget {
           Users({Key? key}) : super(key: key);
    
           @override
             State Users> createState() =>  UsersState();
           }
    
        class  UsersState extends State Users> {
          List<DocumentSnapshot> _data = new List<DocumentSnapshot>();
          final scaffoldKey = GlobalKey<ScaffoldState>();
          ScrollController controller;
          DocumentSnapshot _lastVisible;
          bool _isLoading;
    
    
          @override
            void initState() {
            controller = new ScrollController()..addListener(_scrollListener);
            super.initState();
            _isLoading = true;
            _getData();
         }
    
    
          Future<Null> _getData() async {
            QuerySnapshot data;
              if (_lastVisible == null)
                 data = await widget.firestore
                       .collection('users') //collection name
                       .orderBy('created_at', descending: true)
                        // to retrieve the latest data first
                        .limit(3)
                        .getDocuments();
               else
                 data = await widget.firestore
                .collection('users')//collection name
                .orderBy('created_at', descending: true)
                // to retrieve the latest data first
                .startAfter([_lastVisible['created_at']])
                .limit(3)
                .getDocuments();
    
               if (data != null && data.documents.length > 0) {
                 _lastVisible = data.documents[data.documents.length - 1];
               if (mounted) {
                  setState(() {
                    _isLoading = false;
                    _data.addAll(data.documents);
                  });
                }
              } else {
                  setState(() => _isLoading = false);
                  scaffoldKey.currentState?.showSnackBar(
                  SnackBar(
                    content: Text('No Data'),
               ),
              );
             }
                return null;
               }
    
          @override
            Widget build(BuildContext context) {
              return Scaffold(
                key: scaffoldKey,
                 appBar: new AppBar(),
                 body: RefreshIndicator(
                 child: ListView.builder(
                    controller: controller,
                    itemCount: _data.length + 1,
                    itemBuilder: (_, int index) {
                     if (index < _data.length) {
                      final DocumentSnapshot document = _data[index];
                       return new Container(
                       height: 200.0,
                       child: new Text(document['value']),// get specfic 
                         documentSnapshot, assign the name value(key) here
                       );
                     }
                 return Center(
                  child: new Opacity(
                   opacity: _isLoading ? 1.0 : 0.0,
                   child: new SizedBox(
                    width: 32.0,
                    height: 32.0,
                      child: new CircularProgressIndicator()),
                   ),
                 );
                },
              ),
            onRefresh: ()async{
            _data.clear();
            _lastVisible=null;
             await _getData();
           },
          ),
        );
      }
    
        @override
        void dispose() {
         controller.removeListener(_scrollListener);
         super.dispose();
       }
    
        void _scrollListener() {
         if (!_isLoading) {
          if (controller.position.pixels == controller.position.maxScrollExtent) {
           setState(() => _isLoading = true);
           _getData();
           }
         }
       } 
      }
    

`

Related