繁体   English   中英

Flutter Firestore 分页

[英]Flutter Firestore pagination

我正在尝试使用Firestore进行分页,我阅读了文档,它在Swift中这样实现

let first = db.collection("cities")
    .order(by: "population")
    .limit(to: 25)

first.addSnapshotListener { (snapshot, error) in
    guard let snapshot = snapshot else {
        print("Error retrieving cities: \(error.debugDescription)")
        return
    }

    guard let lastSnapshot = snapshot.documents.last else {
        // The collection is empty.
        return
    }

    // Construct a new query starting after this document,
    // retrieving the next 25 cities.
    let next = db.collection("cities")
        .order(by: "population")
        .start(afterDocument: lastSnapshot)

    // Use the query for pagination.
    // ...
}

只是为了练习,我尝试获取三个文档,如果点击按钮,再获取一个文档。

 Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').limit(3).getDocuments().then((snapshot) {
     _lastDocument = snapshot.documents.last;
     snapshot.documents.forEach((snap) {
        print(snap.data);
     });
   });

点击按钮后像这样尝试。

 Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
     snapshot.documents.forEach((snap) {
        print(snap.data);
      });
     });

但是控制台这样说。

处理手势时抛出以下断言:类型“DocumentSnapshot”不是类型“List[dynamic]”的子类型

为什么我必须通过列表?

有谁知道如何解决这一问题?

更新

我能够像这样分页。

class PaginationExample extends StatefulWidget {
  @override
  _PaginationExampleState createState() => _PaginationExampleState();
}

class _PaginationExampleState extends State<PaginationExample> {
  var _restaurants = <Restaurant>[];
  var _nomore = false;
  var _isFetching = false;
  DocumentSnapshot _lastDocument;
  ScrollController _controller;


  void _fetchDocuments() async {
    final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').limit(8).getDocuments();
    // your logic here
  }

  Future<Null> _fetchFromLast() async {
    final QuerySnapshot querySnapshot = await Firestore.instance.collection('restaurants').orderBy('likes').startAfter([_lastDocument['likes']]).limit(4).getDocuments();
      if (querySnapshot.documents.length < 4) {
          _nomore = true;
          return;
      }
      _lastDocument = querySnapshot.documents.last;
      for (final DocumentSnapshot snapshot in querySnapshot.documents) {
        final Restaurant re = Restaurant(snapshot);
        _restaurants.add(re);
      }
      setState(() {});
  }

  void _scrollListener() async {
    if (_nomore) return;
    if (_controller.position.pixels == _controller.position.maxScrollExtent && _isFetching == false) {
        _isFetching = true;
        await _fetchFromLast();
        _isFetching = false;
    }
  }

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

  @override
  Widget build(BuildContext context) {
    return Container(

    );
  }
}

这里有一个错误:

     Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter(_lastDocument).limit(1).getDocuments().then((snapshot) {
         snapshot.documents.forEach((snap) {
            print(snap.data);
          });
         });

startAfter方法需要一个 List 值 params 并且您正在传递一个DocumentSnapshot

获取一个 [values] 列表,创建并返回一个新的 [Query],它在提供的字段之后开始,相对于查询的顺序。

你可以尝试这样的事情:

 Firestore.instance.collection('user').where('name', isEqualTo: 'Tom').orderBy('age').startAfter([{'name': 'Tom'}]).limit(1).getDocuments().then((snapshot) {
         snapshot.documents.forEach((snap) {
            print(snap.data);
          });
         });

使用此包仅使用 2 个属性、 itemBuilderquery进行分页- paginate_firestore

例如,

      PaginateFirestore(
        itemBuilder: (context, documentSnapshot) => ListTile(
          leading: CircleAvatar(child: Icon(Icons.person)),
          title: Text(documentSnapshot.data['name']),
          subtitle: Text(documentSnapshot.documentID),
        ),
        // orderBy is compulsary to enable pagination
        query: Firestore.instance.collection('users').orderBy('name'),
      )

这对我有用,可以提供实时分页

定义获取数据的函数

import 'package:cloud_firestore/cloud_firestore.dart';

import '../../../core/constants/firebase_constants.dart';

class FirebaseProvider {
  final FirebaseFirestore _firestore;

  FirebaseProvider({required FirebaseFirestore firestore})
      : _firestore = firestore;

  CollectionReference get _posts =>
      _firestore.collection(FirebaseConstants.postsCollection);

  Future<List<DocumentSnapshot>> fetchFirstList(
      String fromgst, String postType) async {
    return (await _posts
            .where("fromgst", isEqualTo: fromgst)
            .where("postType", isEqualTo: postType)
            .orderBy("date", descending: true)
            .limit(5)
            .get())
        .docs;
  }

  Future<List<DocumentSnapshot>> fetchNextList(String fromgst, String postType,
      List<DocumentSnapshot> documentList) async {
    return (await _posts
            .where("fromgst", isEqualTo: fromgst)
            .where("postType", isEqualTo: postType)
            .orderBy("date", descending: true)
            .startAfterDocument(documentList[documentList.length - 1])
            .limit(5)
            .get())
        .docs;
  }
}

单独的 class 来处理分页

import 'dart:async';
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:growmore/features/home/repository/firebase_provider.dart';
import 'package:rxdart/rxdart.dart';

class PostListBloc {
  List<DocumentSnapshot>? documentList;
  bool showIndicator = false;
  FirebaseProvider? firebaseProvider;

  BehaviorSubject<List<DocumentSnapshot>>? postController;
  BehaviorSubject<bool>? showIndicatorController;

  PostListBloc() {
    postController = BehaviorSubject<List<DocumentSnapshot>>();
    showIndicatorController = BehaviorSubject<bool>();
    firebaseProvider = FirebaseProvider(firestore: FirebaseFirestore.instance);
  }

  Stream get getShowIndicatorStream => showIndicatorController!.stream;

  Stream<List<DocumentSnapshot>> get postStream => postController!.stream;

  // This method will automatically fetch first 10 elements from the document list
  Future fetchFirstList(String fromgst, String postType) async {
    try {
      documentList = await firebaseProvider?.fetchFirstList(fromgst, postType);
      print("documentList$documentList");
      postController?.sink.add(documentList!);
      try {
        if (documentList!.isEmpty) {
          postController?.sink.addError("No Data Available");
        }
      } catch (e) {
        print(e);
      }
    } on SocketException {
      postController?.sink.addError(SocketException("No Internet Connection"));
    } catch (e) {
      print(e.toString());
      postController?.sink.addError(e);
    }
  }

  //This will automatically fetch the next 10 elements from the list
  fetchNextPosts(String fromgst, String postType) async {
    try {
      updateIndicator(true);
      List<DocumentSnapshot> newDocumentList = await firebaseProvider!
          .fetchNextList(fromgst, postType, documentList!);
      print('asca$newDocumentList');
      documentList!.addAll(newDocumentList);
      postController!.sink.add(documentList!);
      try {
        if (documentList!.isEmpty) {
          postController!.sink.addError("No Data Available");
          updateIndicator(false);
        }
      } catch (e) {
        updateIndicator(false);
      }
    } on SocketException {
      postController!.sink.addError(SocketException("No Internet Connection"));
      updateIndicator(false);
    } catch (e) {
      updateIndicator(false);
      print(e.toString());
      postController!.sink.addError(e);
    }
  }

  //For updating the indicator below every list and paginate*
  updateIndicator(bool value) async {
    showIndicator = value;
    showIndicatorController!.sink.add(value);
  }

  void dispose() {
    postController!.close();
    showIndicatorController!.close();
  }
}

用户界面部分


  ScrollController controller = ScrollController();

  @override
  void initState() {
    super.initState();
    postListBloc = PostListBloc();
    print("dvvfe${widget.fromgst}");
    postListBloc!.fetchFirstList(widget.fromgst, widget.postType);
    controller.addListener(_scrollListener);
  }

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<List<DocumentSnapshot>>(
        stream: postListBloc!.postStream,
        builder: (context, snapshot) {
          if (snapshot.data != null) {
            return ListView.builder(
              itemCount: snapshot.data?.length,
              shrinkWrap: true,
              controller: controller,
              itemBuilder: (context, index) {
                return Card(
                  child: Padding(
                    padding: const EdgeInsets.all(8.0),
                    child: ListTile(
                      title: Text(snapshot.data![index]['description']),
                    ),
                  ),
                );
              },
            );
          } else {
            return const CircularProgressIndicator();
          }
        },
      ),
    );
  }

  void _scrollListener() {
    if (controller.offset >= controller.position.maxScrollExtent &&
        !controller.position.outOfRange) {
      print("Cavc$controller");
      print("at the end of list");
      postListBloc!.fetchNextPosts(widget.fromgst, widget.postType);
    }
  }
}

我发现它没有开源 github repo

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM