Mar*_*rco 9 caching dart firebase flutter google-cloud-firestore
在我的应用程序中,我使用来自 Firebase 的数据构建了一个对象列表。在 StreamBuilder 中,我检查快照是否有数据。如果没有,我将返回一个带有“正在加载...”的简单文本小部件。我的问题是,如果我转到应用程序中的另一个页面,然后再回来,您会在一瞬间看到屏幕中间显示“正在加载...”,这有点烦人。我很确定它正在从 Firebase 下载数据,并在我每次返回该页面时构建小部件。如果我不检查数据,它会给我一个我试图从 null 访问数据的数据。
有没有办法缓存已经下载的数据,如果来自 Firebase 的数据没有变化,那么就使用缓存的数据?
这是我的代码的编辑版本:
class Schedule extends StatefulWidget implements AppPage {
final Color color = Colors.green;
@override
_ScheduleState createState() => _ScheduleState();
}
class _ScheduleState extends State<Schedule> {
List<Event> events;
List<Event> dayEvents;
int currentDay;
Widget itemBuilder(BuildContext context, int index) {
// Some Code
}
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder(
stream: Firestore.instance.collection('events').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading...");
}
events = new List(snapshot.data.documents.length);
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot doc = snapshot.data.documents.elementAt(i);
events[i] = Event(
name: doc["name"],
start: DateTime(
doc["startTime"].year,
doc["startTime"].month,
doc["startTime"].day,
doc["startTime"].hour,
doc["startTime"].minute,
),
end: DateTime(
doc["endTime"].year,
doc["endTime"].month,
doc["endTime"].day,
doc["endTime"].hour,
doc["endTime"].minute,
),
buildingDoc: doc["location"],
type: doc["type"],
);
}
events.sort((a, b) => a.start.compareTo(b.start));
dayEvents = events.where((Event e) {
return e.start.day == currentDay;
}).toList();
return ListView.builder(
itemBuilder: itemBuilder,
itemCount: dayEvents.length,
);
},
),
);
}
}
Run Code Online (Sandbox Code Playgroud)
Jam*_*ron 14
您可以使用以下代码来定义要从中检索数据的源。这将在本地缓存或服务器上搜索,而不是两者都搜索。它适用于所有get()参数,无论是搜索还是文档检索。
import 'package:cloud_firestore/cloud_firestore.dart';
FirebaseFirestore.instance.collection("collection").doc("doc").get(GetOptions(source: Source.cache))
Run Code Online (Sandbox Code Playgroud)
要检查搜索在缓存中是否有数据,您需要首先针对缓存运行搜索,如果没有结果,则针对服务器运行搜索。我发现项目firestore_collection使用了一个简洁的扩展,可以大大简化这个过程。
import 'package:cloud_firestore/cloud_firestore.dart';
// https://github.com/furkansarihan/firestore_collection/blob/master/lib/firestore_document.dart
extension FirestoreDocumentExtension on DocumentReference {
Future<DocumentSnapshot> getSavy() async {
try {
DocumentSnapshot ds = await this.get(GetOptions(source: Source.cache));
if (ds == null) return this.get(GetOptions(source: Source.server));
return ds;
} catch (_) {
return this.get(GetOptions(source: Source.server));
}
}
}
// https://github.com/furkansarihan/firestore_collection/blob/master/lib/firestore_query.dart
extension FirestoreQueryExtension on Query {
Future<QuerySnapshot> getSavy() async {
try {
QuerySnapshot qs = await this.get(GetOptions(source: Source.cache));
if (qs.docs.isEmpty) return this.get(GetOptions(source: Source.server));
return qs;
} catch (_) {
return this.get(GetOptions(source: Source.server));
}
}
Run Code Online (Sandbox Code Playgroud)
如果添加此代码,您只需将.get()文档和查询的命令更改为.getSavy(),它会首先自动尝试缓存,并且仅在本地找不到数据时才联系服务器。
FirebaseFirestore.instance.collection("collection").doc("doc").getSavy();
Run Code Online (Sandbox Code Playgroud)
要确定数据是来自 Firestore 的本地缓存还是来自网络,您可以执行以下操作:
for (int i = 0; i < snapshot.data.documents.length; i++) {
DocumentSnapshot doc = snapshot.data.documents.elementAt(i);
print(doc.metadata.isFromCache ? "NOT FROM NETWORK" : "FROM NETWORK");
Run Code Online (Sandbox Code Playgroud)
在您描述的情况下,您可能仍会看到加载屏幕“不是来自网络”。这是因为从本地缓存中获取它确实需要一些时间。很快,您将能够为结果为空的案例请求查询的元数据。
就像其他人建议的那样,您可以缓存结果,而不会看到这一点。首先,您可以尝试使用以下方法将其缓存在 Widget 中:
QuerySnapshot cache; //**
@override
Widget build(BuildContext context) {
return Center(
child: StreamBuilder(
initialData: cache, //**
stream: Firestore.instance.collection('events').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Text("Loading...");
}
cache = snapshot.data; //**
Run Code Online (Sandbox Code Playgroud)
这将使您的小部件记住数据。但是,如果这不能解决您的问题,您就不必将其保存在此小部件中,而是将其保存在其他地方。一种选择是使用Provider小部件将其存储在超出此特定小部件范围的变量中。
可能不相关,但将 to 移动Firestore.instance.collection('events').snapshots()到initState(),将流的引用保存在私有字段中并使用它也是一个好主意StreamBuilder。否则,在每次 build() 时,您可能会创建一个新流。无论出于build()何种原因,您都应该为每秒发生多次的调用做好准备。
| 归档时间: |
|
| 查看次数: |
9190 次 |
| 最近记录: |