How do i add more items on scroll in the listview?

1,189

Not sure if the following will help, but here's what I did in my flutter app. Pay special attention to the _onScroll function at the bottom.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../blocs/feed_bloc/feed_events.dart';
import '../../blocs/feed_bloc/bloc.dart';
import '../../blocs/feed_bloc/feed_state.dart';
import './post.dart';
import '../loader.dart';

class Feed extends StatefulWidget {
  Feed({ Key key }): super(key: key);

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

class _FeedState extends State<Feed> {
  final _scrollController = ScrollController();
  final _scrollThreshold = 300.0;
  FeedBloc _feedBloc;

  _FeedState();

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
    _feedBloc = BlocProvider.of<FeedBloc>(context);
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<FeedBloc, FeedState>(
      builder: (context, state) {
        if (state is FeedUninitialized) {
          _feedBloc.add(Init());
          return Loader();
        }

        if (state is FeedLoading) {
          return Loader();
        }

        if (state is FeedLoaded) {
          return new RefreshIndicator(
            child: ListView(
              controller: _scrollController,
              // Important: Remove any padding from the ListView.
              padding: EdgeInsets.zero,
              children: state.posts.map((post) {
                return Post(
                  postModel: state.postMap[post]
                );
              }).toList(),
            ),
            onRefresh: () {
              _feedBloc.add(Refresh());
              return new Future.delayed(new Duration(seconds: 1));
            },
          );
        }


        return Center(
          child: SizedBox(
            height: 33,
            child: Text(
              'Sorry, there was a problem loading the feed.',
              style: TextStyle( color: Colors.white)
            ),
          ),
        );
      }
    );
  }

  int lastScroll = 0;
  void _onScroll() {
    if (new DateTime.now().millisecondsSinceEpoch - lastScroll > 1000) {
      lastScroll = new DateTime.now().millisecondsSinceEpoch;
      final maxScroll = _scrollController.position.maxScrollExtent;
      final currentScroll = _scrollController.position.pixels;
      if (maxScroll - currentScroll <= _scrollThreshold) {
        _feedBloc.add(Fetch());
      }
    }
  }
}

I think what you need is startAfterDocument. See below...

Future<Map<String, dynamic>> getPosts({PostModel parent, PostModel lastPost}) async {
    // First, we create the initial query. 
    // We pass in the parent key if there is one.
    Query query = Firestore.instance
      .collection('posts');

      if (parent != null) {
        query = query.where('parentId', isEqualTo: parent.key);
      } else {
        query = query.where('parentId', isNull: true);
      }

      query = query.orderBy('timestamp', descending: true);

    // If we want to start at a certain doc (user is scrolling throught he feed)
    // then we specify to start after a certain doc
    if (lastPost != null) {
      query = query.startAfterDocument(lastPost.rawDoc);
    }

    // Execute the query
    final results = await query
      .limit(18)
      .getDocuments();

    // Create some Lists/Maps we're going to need
    List<String> posts = new List<String>();
    Map<String, PostModel> postMap = new Map<String, PostModel>();
    Map<String, bool> likeMap = new Map<String, bool>();

    // Convienience/Brevity 
    final List<DocumentSnapshot> documents = results.documents;
    final int numDocs = documents.length;

    if (numDocs > 0) {
      final firstDoc = results.documents[0].data;
      final lastDoc = results.documents[numDocs-1].data;

      // Get all your likes for a certain date range that aligns with the
      // documents we just retrieved
      likeMap = await getLikes(firstDoc['postCreated'], lastDoc['postCreated']);
    }

    // Loop through, and create all the PostModels
    for (int i = 0; i < numDocs; i++) {
      DocumentSnapshot doc = documents[i];
      PostModel post = snapshotToPostModel(doc, likeMap[doc.documentID]);
      postMap[doc.documentID] = post;
      posts.add(post.key);
    }

    final Map<String, dynamic> res = new Map<String, dynamic>();

    res['posts'] = posts;
    res['postMap'] = postMap;

    return res;
  }
Share:
1,189
alex
Author by

alex

Updated on December 19, 2022

Comments

  • alex
    alex over 1 year

    This code here currently loads all the brews that are stored in the Firestore collection.. How can i load initially just 10 brews and then later on when the user scrolls down and reaches to the end of the list of 10 brews..it should load 10 more after the last brew..and the brews should be sorted according to the timestamp.

    class BrewList extends StatefulWidget {
      @override
      _BrewListState createState() => _BrewListState();
    }
    
    class _BrewListState extends State<BrewList> {
      List<Brew> brews = [];
       ScrollController _controller = ScrollController();
    
      @override
      void initState() {
        _startFirst();
        super.initState();
        _controller.addListener(_scrollListener);
      }
    
           _scrollListener() {
        setState(() {
          if (_controller.position.atEdge) {
            if (_controller.position.pixels == 0) {
    
    
            } else {
    
    
    
            }
          }
        });
      }
    
    
    
     _startFirst() async {
        brews = await DatabaseService().brews ?? [];
        setState(() {
          brews = brews;
    
        });
      }
    
      @override
      Widget build(BuildContext context) {
        Future<void> _refreshBrews() async {
          List<Brew> tempList = await DatabaseService().brews;
          setState(() {
            brews = tempList;
          });
          print(brews);
        }
    
    
        return RefreshIndicator(
          onRefresh: _refreshBrews,
          child: ListView.builder(
            controller: _controller,
            itemCount: brews.length,
            itemBuilder: (context, index) {
              return BrewTile(brew: brews[index]);
            },
          ),
        );
      }
    }
    
    
    Future<List<Brew>> get brewss async {
        QuerySnapshot snapshot = await brewsCollection
            .orderBy('timestamp', descending: true)
              .limit(2)
            .getDocuments();
    
        return _postListFromSnapshot(snapshot);
      }
    
    • Sandeep Sharma
      Sandeep Sharma about 4 years
      In first you want to fetch 10 items only?
    • alex
      alex about 4 years
      it could be 20..but then i want to load more on scroll..
    • pskink
      pskink about 4 years
    • alex
      alex about 4 years
      thank you very much..but could you possibly simplify it a bit..i'm just a beginner here and this looks a bit complex.. @pskink
    • pskink
      pskink about 4 years
      you have a complete working widget, just use it
    • wcyankees424
      wcyankees424 about 4 years
      hey alex, I see you are still struggling with this I will have an answer for you in a little while
    • alex
      alex about 4 years
      Hey Jim..its been a while.. hope you're alright..@wcyankees424
  • alex
    alex about 4 years
    Future<List<Brew>> get brews async { QuerySnapshot snapshot = await brewsCollection .orderBy('timestamp', descending: true).getDocuments(); return _brewListFromSnapshot(snapshot); }
  • alex
    alex about 4 years
    this is how i get my current list..i've added the scrollController ..is it possible for you to show me the function i need to add here?
  • Chad Lamb
    Chad Lamb about 4 years
    You mean how to get 10 items from firestore at a time?
  • alex
    alex about 4 years
    Initially with the code above..it loads all the brews in the list..but i want to load only 10 brews initially and then when the user reaches the end of the list of initially loaded 10 brews..I want to load 10 more and so on ..
  • Chad Lamb
    Chad Lamb about 4 years
    That has to do with how you can paginate the data which is stored in firestore using some kind of query. I see that you have asked about this before and received a reasonable answer. stackoverflow.com/a/61133438/5211220 Basically you need to be able to write your queries to get documents in chunks of 10. If you have an auto incrementing integer property on the documents you can order by that, then initially select where that property is between 0 and 11 initially. Then on the second query between 10 and 21 etc.
  • alex
    alex about 4 years
    I tried it but thats not working anymore..because i'm ordering the items according to the timestamp as i wrote in the above comment..it'd be highly appreciated if you can edit the above code and make the necessary changes..
  • Chad Lamb
    Chad Lamb about 4 years
    The principle is the same. Query the first 10 items similar to how @Sandeep showed, ordering by the timestamp. Take the last timestamp and query the next 10 items using that timestamp as part of a where clause and a limit. I have not used the firestore api, but a quick google and I came up with something like the following. Dart Firestore.instance.collection('brew').document(items) .where('timeStamp' isLessThan: previousTimestamp).orderBy('timeStamp', descending:true).limit(10);
  • alex
    alex about 4 years
    This doesn't answer my question but thank you for making the effort..i'm trying to find out how to order the next set of data which startsAfter() the last set of data that is initially loaded on the screen.
  • Tyler Biscoe
    Tyler Biscoe about 4 years
    @alex I see. I edited my answer to include the code that actually queries firestore. I think you might need startAftrerDocument. Side note: I feel your pain. The docs for firebase in dart/flutter are not great. Good luck!
  • Tyler Biscoe
    Tyler Biscoe about 4 years
    If my edit does the trick, I'll return later and make my response more concisely answer your question.
  • alex
    alex about 4 years
    yes i need the startAfterDocument let me try and see if this works out..but it was helpful so i'll give an upvote to this..