iT邦幫忙

2025 iThome 鐵人賽

DAY 10
1

大家好~昨天的程式碼,成功地讓靈感卡片「動」了起來,實現了滑動效果~但那更像是一場視覺魔術。當我們滑掉一張卡片時,它只是從畫面上消失,App並沒有真正「記住」這個行為。如果你重新載入 App,那些卡片又會回來,因為我們用的是一個寫死的靜態列表。

這個問題的核心在於狀態(State),App的狀態就是它在任何特定時間點的所有資料,在我們的例子中,「待瀏覽的靈感卡片列表」就是一個核心狀態,當使用者滑動卡片時,這個列表應該要被修改,而UI必須自動更新以反映這個變化。

為了解決這個問題,我們需要引入「狀態管理」。今天將使用Flutter社群中非常受歡迎的套件Riverpod,來為我們的App建立一個可靠的「記憶中樞」。

為什麼選擇 Riverpod?

  • 編譯時安全:不容易寫錯,在編譯階段就能發現問題。
  • 簡單直觀:不依賴BuildContext,可以在任何地方存取狀態。
  • 可測試性高:將商業邏輯與UI徹底分離,更容易進行單元測試。
  • 靈活強大:提供多種類型的Provider,應對各種場景。

第一步:安裝與設定Riverpod

在專案根目錄下執行指令來安裝flutter_riverpod:
flutter pub add flutter_riverpod

Riverpod需要在App的最頂層放置一個名為ProviderScope的Widget,這樣整個App內的所有Widget才能存取到我們建立的Provider。

打開lib/main.dart檔案,修改main函式,用ProviderScope包住 MyApp:

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

// ... import your home page

void main() {
  runApp(
    // 用 ProviderScope 包住整個 App
    const ProviderScope(
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // ...
      home: HomePage(),
    );
  }
}

第二步:建立我們的 InsightProvider

Provider是Riverpod的核心,它負責「提供」狀態,對於像列表這樣需要被修改的狀態,最適合的模式是 StateNotifier和StateNotifierProvider。

在 lib/features/insight/ 目錄下建立一個新檔案 insight_provider.dart。

第三步:將 UI 與 Provider 連動

現在的「資料中樞」已經建好了,接下來要讓HomePage去監聽它。

首先需要將HomePage從StatelessWidget或StatefulWidget改為ConsumerWidget或ConsumerStatefulWidget。ConsumerWidget是最簡單的,可以讓build方法多一個WidgetRef參數,這個 ref就是我們與Provider溝通的橋樑。

修改 lib/presentation/home_page.dart
(程式碼太長先看程式碼解析)

程式碼解析:

  • extends ConsumerWidget:告訴Flutter這個Widget需要消費(consume) Provider的狀態。
  • WidgetRef ref:build方法中多了這個參數,它是我們存取Provider的工具。
  • ref.watch(insightNotifierProvider):這是最重要的部分,watch會「監聽」一個 Provider。當 insightNotifierProvider 內部的狀態 (也就是那個 List) 發生變化時,ref.watch 會自動觸發 HomePage 的 build 方法進行重建,從而更新UI。
  • insights.isEmpty ? ...: 我們現在可以輕易地處理各種狀態。如果列表是空的,就顯示提示文字。
  • ref.read(...):在onSwipe回呼函式中,我們使用ref.read。read 只會「讀取」Provider 的當前狀態或它的 Notifier 實例,而不會進行監聽。它適合在按鈕點擊、滑動等一次性事件的回呼中使用,用來觸發狀態的改變。
  • .notifier:我們透過insightNotifierProvider.notifier來存取到InsightNotifier的實例,這樣才能呼叫我們寫好的removeInsight方法。

現在再次執行你的App!會發現,每當你滑動一張卡片,它就真的消失了。即使只剩下最後一張卡片,滑掉後也會正確地顯示「今天沒有新靈感了!」的提示。我們的App就有了記憶!

明日預告:互動按鈕實作

今天成功跨出了狀態管理的第一步,也是最重要的一步。理解了為什麼需要狀態管理,如何設定Riverpod、如何建立StateNotifierProvider、如何在UI中監聽並重建UI等。

將UI邏輯(顯示什麼)和商業邏輯(如何移除卡片)分離開來,這對未來維護和擴充App至關重要。

今天的App已經從一個「動畫展示品」蛻變為一個真正有「狀態」的應用程式。Day 11會將下方的「保留」、「捨棄」、「收藏」按鈕也與我們的Provider連動起來,讓使用者有更多操作的選擇~明天見!


【哈囉你好:)感謝你的閱讀!其他我會常出沒的地方:Threads


上一篇
30 天做一個極簡App:滑動的藝術,實作卡片堆疊與滑動效果
下一篇
30 天做一個極簡App:互動按鈕「一鍵保留、捨棄、收藏」
系列文
Mobile Dev|日更靈感來源 App:Flutter × LLM × n8n,每天只推 3 則!12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言