iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0
Build on Google AI

30 天用 Google AI 打造台灣防災速報 App系列 第 19 篇

Day 19|示警一更新,畫面就跟著變:Flutter 即時監聽 Firestore,以及「沒有資料」和「沒有示警」的差別

  • 分享至 

  • xImage
  •  

系列:30 天用 Google AI 打造臺灣防災速報 App(Day 19/30)

今天做 App 的首頁。要做到三件事:資料一變,畫面自己更新;最嚴重的示警排在最上面;網路斷了,要讓人知道眼前的資料可能不是最新的。

App 首頁

即時監聽

Firestore 的 Flutter 套件可以「監聽」一個集合。集合裡有文件新增、修改或刪除,App 就會收到新資料,畫面跟著重畫,不用自己定時重新下載。

Stream<AlertFeed> watchActive() => _db
    .collection('alerts')
    .snapshots(includeMetadataChanges: true)
    .map((snap) => _feed(
          [for (final d in snap.docs) Alert.fromMap(d.data())],
          fromCache: snap.metadata.isFromCache,
        ));

Stream 是 Dart 裡「會持續送來新資料」的型別。首頁訂閱這個 Stream,每收到一次就更新畫面。

要注意 isFromCache。Firestore 會把讀過的資料存在手機裡,還沒拿到伺服器的資料時(例如剛打開 App,或沒網路),先拿這份本機快取給 App。快取可能是幾小時前的,也可能是空的。includeMetadataChanges: true 不能省:資料內容沒變、只是從「快取」變成「伺服器確認過」時,加了它 App 才會收到通知。下面會用到這個分別。

排序:最嚴重的在最上面

先依等級排,同等級再依更新時間由新到舊。等級依序是紅、橙、黃、藍。

過了有效期限的示警不會馬上消失。卡片變灰、標「已過期」、移到最後,等後端下一輪同步再移走。使用者看到的是「已過期」,不會覺得示警突然不見了。

「沒有資料」不等於「沒有示警」

這是首頁最需要小心的地方。不能只看畫面上有沒有示警,下面四種狀態意思完全不同:

情況 畫面顯示
伺服器確認目前沒有示警 目前沒有生效中的示警
還沒拿到伺服器的資料,本機快取是空的 尚未取得最新資料,請確認網路後重試。
還沒拿到伺服器的資料,只有舊的快取 照樣顯示清單,上方提示「目前顯示的是離線資料,可能不是最新狀態。」
有先前的資料,但監聽回報錯誤 照樣顯示清單,上方提示「更新失敗,目前顯示的是先前的資料。」

如果離線時也顯示「目前沒有生效中的示警」,使用者會以為平安無事。所以畫面要先判斷資料是不是快取、上次更新有沒有失敗,再決定畫面要顯示什麼內容和提示。

還有一種情況只看示警清單看不出來:後端的同步排程停了。手機有網路,監聽也正常,但示警已經好幾個小時沒更新。所以後端每輪同步都會在 meta/sync_status 記下三件事:這輪什麼時候跑、成功沒有、上次成功是什麼時候。App 也監聽這份文件。超過 25 分鐘沒有新的一輪,首頁就提醒「資料可能不是最新」;這輪失敗,就顯示上次成功的時間,提醒從那之後同步都沒有成功。

同一個 id 合成一張卡

Day 16 決定同一個 id 的示警全部保留,結果首頁會出現很多張幾乎一樣的卡,例如一份電文裡有 19 家醫院的門診異動。資料庫照樣保留每一則,只在畫面上合成一張卡,點開再列出每一則。

同一個 id 的四則停水合成一張卡

篩選

上方可以依分類和縣市篩選,選項旁邊直接寫生效中的示警則數(合併成一張卡的,照則數算),數量是 0 的不列出來。

如果同時選了分類和縣市,結果可能是空的。這時要顯示「沒有符合目前篩選的示警。」,不能說「目前沒有生效中的示警」。

過去 24 小時已結束的示警

首頁最下面有一個收合區塊,展開時才去讀已結束的示警。被新版取代的不顯示,免得同一件事出現兩次。

這裡有一個容易踩到的地方。同時用「狀態」和「結束時間」查詢,Firestore 要另建一個複合索引(同時涵蓋兩個欄位的索引)。我們不想另建,所以查詢只用結束時間,狀態在 App 裡過濾。如果先「只讀 50 筆」再過濾,前 50 筆剛好都是被取代的,就會變成「沒有已結束的示警」。所以改成每次讀 200 筆、在手機裡過濾,不夠就再往下讀,直到收滿 50 則或讀完 24 小時。

今日小結

  • 首頁監聽 Firestore,資料一變畫面就更新。
  • 最嚴重的排最上面,過期的變灰、移到最後。
  • 分清楚「確認沒有示警」和「拿不到資料」,後端停擺超過 25 分鐘也會提醒。
  • 資料庫沒辦法先過濾狀態時,不要直接限制讀取筆數;要邊讀邊過濾,收滿為止。

明天 Day 20:使用者說「我很害怕」時,問答該怎麼回。


上一篇
Day 18|不寫 Kotlin 也不寫 Swift:Flutter 同一套程式碼做出 Android 與 iOS App
下一篇
Day 20|使用者說「我很害怕」時,問答該怎麼回:情緒問題與危機安全網
系列文
30 天用 Google AI 打造台灣防災速報 App 共 20 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言