返回網誌
教學2025年11月28日安安科技團隊閱讀時間 7 分鐘

釋放 FIRESTORE 威力:即時同步與
離線持久化

Firestore 不只是一個 JSON 儲存桶——它是一部即時資料引擎,即使裝置離線,也能讓 Flutter 應用程式流暢運作。讓我們由頭到尾把它接通,交付令用戶讚不絕口的體驗。

Firestore 即時同步儀表板
// 簡報

FIRESTORE:即時資料引擎

Firebase Cloud Firestore 內置全球邊緣網絡、衝突解決機制和離線快取。你不再需要輪詢 REST 端點——而是訂閱以毫秒級速度傳播的變更串流。

只要以串流思維設計架構,你的 Flutter widget 就會變成即時儀表板。讓我們看看如何接入這條資料流。

即時資料同步

Firestore 提供兩種主要的讀取 API:

API使用時機
get()一次性快照。適合管理後台、冷啟動,或類似定時任務、無需即時更新的讀取。
snapshots()文件或查詢變更的即時串流。非常適合聊天訊息流、即時訂單簿或 IoT 儀表板。

在 Flutter 中,你幾乎不需要為 Firestore 手動呼叫 setState。取而代之,你以 StreamBuilder 組合 UI,讓它自動響應變化。

live_chat_feed.dart
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 用量儀表板,並為突發流量高峰設定警報。
// 上行鏈路

需要協助擴展 FIREBASE?

我們為高速增長的 Flutter 團隊審核 Firestore 資料結構、優化帳單,並設計即時架構。

與 FIREBASE 專家對話
// 訊號放大

分享這篇文章