
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 用量儀表板,並為突發流量高峰設定警報。


