2

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);
  }
alex
  • 130
  • 2
  • 12

2 Answers2

1

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;
  }
Tyler Biscoe
  • 2,322
  • 3
  • 20
  • 33
  • 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. – alex Apr 23 '20 at 17:04
  • @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 Apr 23 '20 at 17:18
  • If my edit does the trick, I'll return later and make my response more concisely answer your question. – Tyler Biscoe Apr 23 '20 at 17:19
  • 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.. – alex Apr 23 '20 at 17:22
0

You could use a scroll controller and a listener to determine if the user has scrolled to the bottom of the screen. If they have then load the next 10 items from the Database Service.

This would look something like the following.

ScrollController _controller = ScrollController(); 

@override
void initState() {
  super.initState();
  // add listener
  _controller.addListener(_scrollListener);
}

_scrollListener() {
  setState(() {
    if (_controller.position.atEdge) {
      if (_controller.position.pixels == 0)
      {
        // user is at top of the list
      }
      else 
      {
        // user is at the bottom of the list.
        // load next 10 items and add them to the list of items in the list.
       }
    }
 });
}
Chad Lamb
  • 564
  • 3
  • 9
  • Future> get brews async { QuerySnapshot snapshot = await brewsCollection .orderBy('timestamp', descending: true).getDocuments(); return _brewListFromSnapshot(snapshot); } – alex Apr 21 '20 at 08:06
  • 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? – alex Apr 21 '20 at 08:08
  • You mean how to get 10 items from firestore at a time? – Chad Lamb Apr 21 '20 at 10:41
  • 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 .. – alex Apr 21 '20 at 11:24
  • 1
    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. https://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. – Chad Lamb Apr 21 '20 at 11:35
  • 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.. – alex Apr 21 '20 at 12:26
  • 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); ``` – Chad Lamb Apr 21 '20 at 14:35