Dart 使用Flatter中的Cloud Firestore创建无限列表

Dart 使用Flatter中的Cloud Firestore创建无限列表,dart,google-cloud-firestore,flutter,Dart,Google Cloud Firestore,Flutter,我目前正在将CloudFireStore与Streambuilder小部件一起使用,以便用Firestore文档填充ListView小部件 new StreamBuilder<QuerySnapshot>( stream: Firestore.instance.collection('videos').limit(10).snapshots(), builder: (BuildContext context, AsyncSnapshot<QuerySnapshot>

我目前正在将CloudFireStore与Streambuilder小部件一起使用,以便用Firestore文档填充ListView小部件

new StreamBuilder<QuerySnapshot>(
  stream: Firestore.instance.collection('videos').limit(10).snapshots(),
  builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
    if (!snapshot.hasData) return new Center(
      child: new CircularProgressIndicator(),
    );
    return new ListView(
      children: snapshot.data.documents.map((DocumentSnapshot document) {
        new Card(child: ...)
      }).toList(),
    );
  },
);
新建StreamBuilder(
流:Firestore.instance.collection('videos').limit(10).snapshots(),
生成器:(BuildContext上下文,异步快照){
如果(!snapshot.hasData)返回新中心(
子项:新的CircularProgressIndicator(),
);
返回新的ListView(
子项:snapshot.data.documents.map((DocumentSnapshot文档){
新卡(子卡:…)
}).toList(),
);
},
);
但是,此设置仅允许查询第一个x结果(在本例中x=10),x是一个固定的数字,用户向下滚动时希望看到的卡片小部件数量迟早会超过x

现在是否可以查询第一个x结果,并在用户点击滚动阈值后从Cloud Firestore查询下一个x+10结果等等?

这将允许动态列表长度,这也有利于Firestore数据的使用

这当然是可能的,但是API中没有预先构建的内容

您必须记住第一页上的最后一个文档,然后使用该文档获取第二页文档


请参阅上的文档。

我不确定是否可以使用Streambuilder。我使用
startAfter
方法在我的应用程序中集成了类似的功能,如下所示

class Feed extends StatefulWidget {
  Feed({this.firestore});

  final Firestore firestore;

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

class _FeedState extends State<Feed> {
  ScrollController controller;
  DocumentSnapshot _lastVisible;
  bool _isLoading;
  CollectionReference get homeFeeds => widget.firestore.collection('homefeed');
  List<DocumentSnapshot> _data = new List<DocumentSnapshot>();
  final scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  void initState() {
    controller = new ScrollController()..addListener(_scrollListener);
    super.initState();
    _isLoading = true;
    _getData();
  }

  Future<Null> _getData() async {
//    await new Future.delayed(new Duration(seconds: 5));
    QuerySnapshot data;
    if (_lastVisible == null)
      data = await widget.firestore
          .collection('homefeed')
          .orderBy('created_at', descending: true)
          .limit(3)
          .getDocuments();
    else
      data = await widget.firestore
          .collection('homefeed')
          .orderBy('created_at', descending: true)
          .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 more posts!'),
        ),
      );
    }
    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['question']),
            );
          }
          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();
      }
    }
  }
}
类提要扩展StatefulWidget{
提要({this.firestore});
最终消防仓库消防仓库;
@凌驾
_FeedState createState();
}
类_FeedState扩展状态{
滚动控制器;
文档快照_lastVisible;
bool_卸载;
CollectionReference get homeFeeds=>widget.firestore.collection('homefeed');
列表_data=新列表();
最终脚手架键=GlobalKey();
@凌驾
void initState(){
控制器=新的ScrollController()…addListener(\u scrollListener);
super.initState();
_isLoading=true;
_getData();
}
Future\u getData()异步{
//等待新的未来。延迟(新的持续时间(秒:5));
查询快照数据;
如果(_lastVisible==null)
data=wait widget.firestore
.collection('homefeed')
.orderBy('created_at',降序:true)
.限额(3)
.getDocuments();
其他的
data=wait widget.firestore
.collection('homefeed')
.orderBy('created_at',降序:true)
.startAfter([\u lastVisible['created\u at']]
.限额(3)
.getDocuments();
if(data!=null&&data.documents.length>0){
_lastVisible=data.documents[data.documents.length-1];
如果(已安装){
设置状态(){
_isLoading=false;
_data.addAll(数据文件);
});
}
}否则{
设置状态(()=>_isLoading=false);
scaffoldKey.currentState?.showSnackBar(
小吃条(
内容:Text('No more posts!'),
),
);
}
返回null;
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
钥匙:脚手架钥匙,
appBar:新的appBar(),
正文:刷新指示器(
子项:ListView.builder(
控制器:控制器,
itemCount:_data.length+1,
itemBuilder:(\ux,int索引){
如果(索引<_数据长度){
最终文档快照文档=_数据[索引];
退回新货柜(
高度:200.0,
儿童:新文本(文件[‘问题]),
);
}
返回中心(
子对象:新的不透明度(
不透明度:_isLoading?1.0:0.0,
孩子:新尺寸的盒子(
宽度:32.0,
身高:32.0,
子项:新的CircularProgressIndicator()),
),
);
},
),
onRefresh:()异步{
_data.clear();
_lastVisible=null;
等待_getData();
},
),
);
}
@凌驾
无效处置(){
控制器。RemovelListener(_scrollListener);
super.dispose();
}
void_scrollListener(){
如果(!\u正在加载){
if(controller.position.pixels==controller.position.maxScrollExtent){
设置状态(()=>_isLoading=true);
_getData();
}
}
}
}

希望有帮助

这当然是可能的,但API中没有预先构建的内容。您必须记住第一页上的最后一个文档,然后使用该文档
startAfter()
,才能获得第二页文档。非常感谢您的回答!滚动阈值是否必须通过滚动控制器实现,或者无限列表最常用的解决方案是什么?刚刚在FirebaseUI中添加了一个分页适配器。为了获得灵感,我建议大家看看这个:这是为flatter@FrankvanPuffelen设计的,你不能用这个来回答。颤振没有所有的Java实现,这就是问题所在。人们必须开始真正回答有关stackoverflow的问题并停止寻找这些Medals此解决方案无法解决问题,就好像您试图动态更改“limit”值一样,流会进入“connection.waiting”状态一小会儿,在这一过程中,您将无法显示任何数据。此代码非常有趣,需要大量的解释,请注意,如果您将查询中的分页限制设置为小于屏幕大小,则scroll listener将永远不会被调用,并且无法加载数据,因此,要么您加载的内容超过用户屏幕,要么用户将仅限于第一页查询,这需要修复,为什么如果用户将scrolllistener刷出绑定,它永远不会被调用?