
FIRESTORE:实时数据引擎
Firebase Cloud Firestore 内置全球边缘网络、冲突解决机制和离线缓存。你不再需要轮询 REST 端点——而是订阅以毫秒级速度传播的变更流。
只要以流式思维设计架构,你的 Flutter widget 就会变成实时仪表盘。让我们看看如何接入这条数据流。
实时数据同步
Firestore 提供两种主要的读取 API:
| API | 使用时机 |
|---|---|
| get() | 一次性快照。适合管理后台、冷启动,或类似定时任务、无需实时更新的读取。 |
| snapshots() | 文档或查询变更的实时流。非常适合聊天消息流、实时订单簿或 IoT 仪表盘。 |
在 Flutter 中,你几乎不需要为 Firestore 手动调用 setState。取而代之,你用 StreamBuilder 组合 UI,让它自动响应变化。
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class LiveChatFeed extends StatelessWidget {
const LiveChatFeed({super.key, required this.roomId});
final String roomId;
@override
Widget build(BuildContext context) {
final query = FirebaseFirestore.instance
.collection('rooms')
.doc(roomId)
.collection('messages')
.orderBy('sentAt', descending: true);
return StreamBuilder<QuerySnapshot<Map<String, dynamic>>>(
stream: query.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(child: Text('Something went wrong'));
}
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
final docs = snapshot.data!.docs;
return ListView.builder(
reverse: true,
itemCount: docs.length,
itemBuilder: (context, index) {
final data = docs[index].data();
return ListTile(
title: Text(data['author'] as String? ?? 'Anon'),
subtitle: Text(data['text'] as String? ?? ''),
trailing: Text(_humanize(data['sentAt'])),
);
},
);
},
);
}
String _humanize(Timestamp? ts) {
if (ts == null) return '';
return DateTime.fromMillisecondsSinceEpoch(ts.millisecondsSinceEpoch)
.toLocal()
.toIso8601String();
}
}整个循环就是这样:Firestore 推送增量变更,Flutter 重建 widget 树,用户即时看到更新。
离线持久化
Firestore 会自动缓存文档,即使地铁驶进隧道,你的应用依然照常运行。读取会先命中本地缓存,待网络恢复后再与服务器同步。
- 离线写入会在本地排队。Firestore 会分配临时 ID,并将它们标记为待处理。
- 设备重新联网后,待处理的写入会按原有顺序重放,流监听器随即发出更新后的快照。
- 你可以检查
snapshot.metadata.isFromCache来调整 UI(例如显示“离线”标记)。
这种默认行为让每个 Flutter 页面都变成渐进式 Web 应用——不需要额外的 Redis,也不需要自建同步 worker。
感觉零延迟的乐观 UI
乐观 UI 指在服务器确认成功之前就先更新界面。Firestore 让这种做法变得安全,因为写入会快速完成,冲突也会以确定性方式合并。
例如:用户发送聊天消息时,立即将消息加到列表,并附上“发送中……”标记。如果 Firestore 拒绝写入(安全规则或网络问题),就将标记换成“重试”,而不阻塞其余数据流。
这种模式让 CRUD 应用蜕变成令人愉悦的体验。延迟隐藏在流畅自信的界面动效背后,用户始终感到一切尽在掌握。
最佳实践与防护栏
- 有意识地反规范化:Firestore 是 NoSQL。将读取频繁的数据(例如作者名称、头像)复制到每个文档中,避免扇出式读取。
- 保持文档轻巧:每个文档保持在 1 MB 以下,并优先使用浅层集合。将大型信息流拆分为分页的子集合。
- 为监听器编写安全规则:编写与查询相互对应的 allow 条款。例如:
allow read: if request.query.limit <= 100 && resource.data.roomId in get(/databases/(default)/documents/users/$(request.auth.uid)).data.rooms;这确保实时监听器只会推送用户有权查看的文档。 - 监控用量:实时监听器会计入活跃连接。善用 Firestore 用量仪表盘,并为突发流量高峰设置警报。


