【问题标题】:Pagination in Flutter with Firebase Realtime Database使用 Firebase 实时数据库在 Flutter 中进行分页
【发布时间】:2019-06-25 04:03:13
【问题描述】:

我正在尝试使用 firebase 实时数据库在 Flutter 中进行分页。 我已经在 Firestore 中尝试过,它在那里工作得很好,但我希望它与实时数据库一起使用。

我是第一次这样获取数据。

 Widget buildListMessage() {
    return Flexible(
      child: StreamBuilder(
        stream: _firebase.firebaseDB
            .reference()
            .child("chats")
            .child("nsbcalculator")
            .orderByChild('timestamp')
            .limitToFirst(15)
            .onValue,
        builder: (context, AsyncSnapshot<Event> snapshot) {
          if (!snapshot.hasData) {
            return Center(
                child: CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(themeColor)));
          } else {

            if (snapshot.data.snapshot.value != null) {

                listMessage = Map.from(snapshot.data.snapshot.value)
                    .values
                    .toList()
                      ..sort(
                          (a, b) => a['timestamp'].compareTo(b['timestamp']));

              if (lastVisible == null) {
                lastVisible = listMessage.last;
                listMessage.removeLast();
              }
            }

            return ListView.builder(
              ...
            );
          }
        },
      ),
    );
  }

在分页之后,我使用带有 ScrollController 的侦听器

  void _scrollListener() async {
    if (listScrollController.position.pixels ==
        listScrollController.position.maxScrollExtent) {
      _fetchMore();
    }
  }

最后

  _fetchMore() {
    _firebase.firebaseDB
        .reference()
        .child("chats")
        .child("nsbcalculator")
        .orderByChild('timestamp')
        .startAt(lastVisible['timestamp'])
        .limitToFirst(5)
        .once()
        .then((snapshot) {

      List snapList = Map.from(snapshot.value).values.toList()
        ..sort((a, b) => a['timestamp'].compareTo(b['timestamp']));


      if (snapList.isNotEmpty) {
        print(snapList.length.toString());

        if (!noMore) {

          listMessage.removeLast();

          //Problem is here.....??
          setState(() {
            listMessage..addAll(snapList);
          });

          lastVisible = snapList.last;

          print(lastVisible['content']);
        }

        if (snapList.length < 5) {
          noMore = true;
        }
      }
    });
  }

它作为实时通信工作正常,但是当我尝试在 _fetchMore() 中进行分页时,调用了 setState 但它刷新了整个小部件的状态并再次重新启动 StreamBuilder,并且所有数据仅被新查询替换。我怎样才能防止这种情况发生??

【问题讨论】:

  • 你能解决吗?

标签: firebase firebase-realtime-database pagination flutter


【解决方案1】:

调用setState 将重绘整个小部件和列表视图。现在,由于您提供了提供第一页的蒸汽,因此重绘后它只是加载它。为避免这种情况,您可以使用自己的流并为其提供新内容。然后您的StreamBuilder 将自动处理更新。

您需要将项目的完整列表存储为一个单独的变量,对其进行更新,然后接收到您的流中。

final _list = List<Event>();
final _listController = StreamController<List<Event>>.broadcast();
Stream<List<Event>> get listStream => _listController.stream;

@override
void initState() {
  super.initState();
  // Here you need to load your first page and then add to your stream
  ...
  _list.addAll(firstPageItems);
  _listController.sink.add(_list);
}

@override
void dispose() {
  super.dispose();
}

Widget buildListMessage() {
    return Flexible(
      child: StreamBuilder(
        stream: listStream
        ...
}

_fetchMore() {
  ...
  // Do your fetch and then just add items to the stream
  _list.addAll(snapList);
  _listController.sink.add(_list);
  ...
}

【讨论】:

  • 这里的_pushController是什么??
  • 对不起,我的错,它的_listController。
  • 好的,兄弟,但我无法解决它.. 事件不是可迭代的.. 我们不能将它与 _list.adAll (事件)一起加入... _list.add (事件)也不起作用。 ..对不起,我没有任何自定义流的经验,所以请解释更多..
  • 从 Firebase 数据库的响应中解析 snapList 后,将其添加到 _list 中。我看到你有 listMessage 变量,也许你可以用它来代替 _list。
【解决方案2】:

试试这个
实时列表分页

class FireStoreRepository {
  final CollectionReference _chatCollectionReference =
      Firestore.instance.collection('Chat');

  final StreamController<List<ChatModel>> _chatController =
      StreamController<List<ChatModel>>.broadcast();

  List<List<ChatModel>> _allPagedResults = List<List<ChatModel>>();

  static const int chatLimit = 10;
  DocumentSnapshot _lastDocument;
  bool _hasMoreData = true;

  Stream listenToChatsRealTime() {
    _requestChats();
    return _chatController.stream;
  }

  void _requestChats() {
    var pagechatQuery = _chatCollectionReference
        .orderBy('timestamp', descending: true)
        .limit(chatLimit);

    if (_lastDocument != null) {
      pagechatQuery =
          pagechatQuery.startAfterDocument(_lastDocument);
    }

    if (!_hasMoreData) return;

    var currentRequestIndex = _allPagedResults.length;

    pagechatQuery.snapshots().listen(
      (snapshot) {
        if (snapshot.documents.isNotEmpty) {
          var generalChats = snapshot.documents
              .map((snapshot) => ChatModel.fromMap(snapshot.data))
              .toList();

          var pageExists = currentRequestIndex < _allPagedResults.length;

          if (pageExists) {
            _allPagedResults[currentRequestIndex] = generalChats;
          } else {
            _allPagedResults.add(generalChats);
          }

          var allChats = _allPagedResults.fold<List<ChatModel>>(
              List<ChatModel>(),
              (initialValue, pageItems) => initialValue..addAll(pageItems));

          _chatController.add(allChats);

          if (currentRequestIndex == _allPagedResults.length - 1) {
            _lastDocument = snapshot.documents.last;
          }

          _hasMoreData = generalChats.length == chatLimit;
        }
      },
    );
  }

  void requestMoreData() => _requestChats();
}

聊天列表视图

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

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

class _ChatViewState extends State<ChatView> {

FireStoreRepository _fireStoreRepository;
final ScrollController _listScrollController = new ScrollController();

@override
  void initState() {
    super.initState();
    _fireStoreRepository = FireStoreRepository();
    _listScrollController.addListener(_scrollListener);
  }

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Flexible(
        child: StreamBuilder<List<ChatModel>>(
          stream: _fireStoreRepository.listenToChatsRealTime(),
          builder: (context, snapshot) {
             return ListView.builder(
              itemCount: snapshot.data.length,
              controller: _listScrollController,
              shrinkWrap: true,
              reverse: true,
              itemBuilder: (context, index) {
                ...
              }
            );
          }
        )
      ),
    );
  }

  void _scrollListener() {
    if (_listScrollController.offset >=
            _listScrollController.position.maxScrollExtent &&
        !_listScrollController.position.outOfRange) {
      _fireStoreRepository.requestMoreData();
    }
  }

}

ChatModel 类

class ChatModel {
  final String userID;
  final String message;
  final DateTime timeStamp;

  ChatModel({this.userID, this.message, this.timeStamp});

  //send 
  Map<String, dynamic> toMap() {
    return {
      'userid': userID,
      'message': message,
      'timestamp': timeStamp,
    };
  }

  //fetch
  static ChatModel fromMap(Map<String, dynamic> map) {
    if (map == null) return null;

    return ChatModel(
      userID: map['userid'],
      message: map['message'],
      timeStamp: DateTime.fromMicrosecondsSinceEpoch(map['timestamp'] * 1000),
    );
  }
}

【讨论】:

  • 这个问题是关于 Firebase 实时数据库的,但你说的是 Firestore。
  • 非常感谢@Tejas Patel。你用分页节省了我在 relatime msg 上的时间
猜你喜欢
  • 2020-10-18
  • 1970-01-01
  • 1970-01-01
  • 2020-04-27
  • 2019-02-12
  • 2020-12-15
  • 2020-07-07
  • 2019-08-14
相关资源
最近更新 更多