Flutter:延迟加载来自 Firestore 的数据
Posted
技术标签:
【中文标题】Flutter:延迟加载来自 Firestore 的数据【英文标题】:Flutter: Lazy Load data from firestore 【发布时间】:2019-10-18 02:09:58 【问题描述】:注意:我已经看到关于 ListView 的延迟加载的答案,但这些答案是针对自定义 API 而不是 firestore 数据库!
我有一个书籍摘要应用程序,应用程序从我的 Firebase/Firestore
数据库中获取数据,然后使用包裹在 StreamBuilder
中的 ListView.builder
显示它。
现在,我想延迟获取数据,我的意思是当用户滚动列表时,所需的数据会被加载,而不是一次加载数据然后延迟显示。
//The Widget used to display data:
Widget feed()
return Container(
width: deviceWidth,
height: deviceHeight / 3,
child: StreamBuilder(
stream: Firestore.instance
.collection('feedItem')
.orderBy('feedId', descending: true)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot)
if (snapshot.hasData)
int totalLength = snapshot.data.documents.length;
return ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: totalLength > 10 ? 10 : totalLength,
itemBuilder: (BuildContext context, int index)
return Container(
width: deviceWidth / 2.5,
child: GestureDetector(
onTap: ()
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => FeedIntro(
snapshot.data.documents[
((totalLength - 1) - index)]['feedId'])));
,
child: Card(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Container(
// width: 150,
height: 150,
foregroundDecoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
snapshot.data.documents[index]['feedImage'],
),
fit: BoxFit.fill)),
),
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(snapshot.data.documents[index]['title']),
)),
],
)),
),
);
,
);
else if (snapshot.hasError)
return Center(child: Text('Sorry Something went wrong!'));
else
return Center(
child: SizedBox(
child: CircularProgressIndicator(),
width: 50,
height: 50,
),
);
),
);
【问题讨论】:
你所谓的“延迟加载”,很多人称之为“分页”。 Firestore 具有这种能力。 firebase.google.com/docs/firestore/query-data/query-cursors 要添加到@DougStevenson 评论,您可以将其与flutter firestore API(我认为firebase 团队尚未添加Dart 示例)从这里配对:pub.dev/documentation/cloud_firestore/latest/cloud_firestore/… 此外,这些方法是最近添加的到 Dart firestore 插件(dart 实现的不完整 API 可能是为什么团队尚未添加 dart 示例)。 :) 感谢您抽出宝贵时间!我知道了,我可以使用 limit 或 startAfter 来分段获取数据,但是对于我应该使用什么方法来集成,这与我当前的代码(StreamBuilder 和 ListView.Builder)有什么建议吗? @RajDhakad 有任何解决方案吗? @ShrutiRamnandanSharma 据我所知,firestore 中的所有数据都已缓存,因此在第一次(未缓存时)后,它会自动延迟获取。 【参考方案1】:您对延迟加载的描述似乎与分页匹配。这是一个在 ListView.builder
中使用带有分页的 Firestore 的简单演示
此示例实现了来自 Firebase 官方文档的 sn-ps,用于 Firestore pagination。
在这个演示中,有两种方法可以在视图上加载数据。
使用刷新整个ListView
RefreshIndicator
向下滚动以点击列表底部以加载ListView
中的下一个文档。
ScrollController
用于确定用户是否点击了列表的底部。
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'DocObj.dart';
Future<void> main() async
WidgetsFlutterBinding.ensureInitialized();
// Initialize Firebase
await Firebase.initializeApp();
runApp(MyApp());
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
class MyHomePage extends StatefulWidget
MyHomePage(Key key, this.title) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
var scrollController = ScrollController();
@override
void initState()
super.initState();
getDocuments();
scrollController.addListener(()
if (scrollController.position.atEdge)
if (scrollController.position.pixels == 0)
print('ListView scroll at top');
else
print('ListView scroll at bottom');
getDocumentsNext(); // Load next documents
);
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: listDocument.length != 0
? RefreshIndicator(
child: ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
controller: scrollController,
itemCount: listDocument.length,
itemBuilder: (context, index)
return ListTile(
title: Text('$listDocument[index].documentName'),
);
,
),
onRefresh: getDocuments, // Refresh entire list
)
: CircularProgressIndicator(),
),
);
List<DocObj> listDocument;
QuerySnapshot collectionState;
// Fetch first 15 documents
Future<void> getDocuments() async
listDocument = List();
var collection = FirebaseFirestore.instance
.collection('sample_pagination')
.orderBy("name")
.limit(15);
print('getDocuments');
fetchDocuments(collection);
// Fetch next 5 documents starting from the last document fetched earlier
Future<void> getDocumentsNext() async
// Get the last visible document
var lastVisible = collectionState.docs[collectionState.docs.length-1];
print('listDocument legnth: $collectionState.size last: $lastVisible');
var collection = FirebaseFirestore.instance
.collection('sample_pagination')
.orderBy("name").startAfterDocument(lastVisible).limit(5);
fetchDocuments(collection);
fetchDocuments(Query collection)
collection.get().then((value)
collectionState = value; // store collection state to set where to start next
value.docs.forEach((element)
print('getDocuments $element.data()');
setState(()
listDocument.add(DocObj(DocObj.setDocDetails(element.data())));
);
);
);
要解析文档中的数据,您可以为您的对象创建一个模型。
class DocObj
var documentName;
DocObj(DocObj doc)
this.documentName = doc.getDocName();
dynamic getDocName() => documentName;
DocObj.setDocDetails(Map<dynamic, dynamic> doc)
: documentName = doc['name'];
该示例处理来自 Firestore 的数据。
这是应用运行时的外观。
【讨论】:
ListView.builder
完成这项工作。一个初学者的错误是在SingleChildScrollView
中添加ListView.builder
,它试图一次加载所有内容,使ListView.builder
无用,或者有时将数据直接加载到SingleChildScrollView
。我希望这个评论可以帮助一些初学者。【参考方案2】:
我遇到了这个问题,我所做的是我在 listview builder 中添加了滚动控制器,我跟踪它是否到达列表底部,如果到达底部,则更新快照的限制。
我已经对此进行了实验并且效果很好。
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class Sample extends StatefulWidget
@override
_SampleState createState() => _SampleState();
class _SampleState extends State<Sample>
ScrollController _chatScrollController;
int loadMoreMsgs = 25; // at first it will load only 25
int a = 50; // 'loadMoreMsgs' will added by 'a' if we load more msgs in listview.
@override
void initState()
_chatScrollController = ScrollController()
..addListener(()
if (_chatScrollController.position.atEdge)
if (_chatScrollController.position.pixels == 0)
print('ListView scrolled to top');
else
setState(()
loadMoreMsgs = loadMoreMsgs + a;
);
print('ListView scrolled to bottom');
);
super.initState();
@override
Widget build(BuildContext context)
return StreamBuilder(
stream: FirebaseFirestore.instance.collection('CollectionName').limit(loadMoreMsgs).snapshots(),
builder: (context, snapshot)
return ListView.builder(
controller: _chatScrollController,
itemBuilder: (context, index)
return Text('This is a sample');
,
);
,
);
我只写了理解所需的代码。
【讨论】:
以上是关于Flutter:延迟加载来自 Firestore 的数据的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:使用来自 Firestore 的多集合流创建动态更新的 DataTable
来自Json的Flutter Firestore返回Null?
在 Flutter 中的垂直 ScrollView 内的水平 ListView 中显示来自 Firestore 的数据
使用来自firestore数据库的geoflutterfire查询结果,使用flutter应用程序返回null或啥都没有,但语法没有错误