iT邦幫忙

2025 iThome 鐵人賽

DAY 11
1

日安(換個方式打招呼)!經過Day 10的努力,App已經脫胎換骨。Riverpod 的加持,每一次滑動不再是過眼雲煙,而是會真實地改變App狀態的有效操作。

滑動操作雖然流暢直觀,但優秀的UX設計通常會為核心功能提供多種入口。有些使用者可能更喜歡明確的點擊按鈕。因此今天的任務是根據UI Wireframe的規劃,在卡片下方加入一排互動按鈕:「捨棄」、「收藏」、和「保留」。

我們將會學習如何透過Controller來命令卡片進行滑動,並實作全新的「收藏」功能,讓使用者可以將真正有價值的靈感永久保存下來。

第一步:用CardSwiperController控制你的卡片

要實現點擊按鈕來觸發卡片滑動,最優雅的方式就是使用flutter_card_swiper套件提供的 CardSwiperController。這個控制器可以讓我們從外部程式碼(例如按鈕的onPressed事件)去命令卡片疊執行swipeLeft、swipeRight等動作。

Controller是一個有狀態的物件,需要被初始化和持有,所以需要將昨天的HomePage從ConsumerWidget升級為ConsumerStatefulWidget。

一、修改HomePage結構

// 將 HomePage 改為 ConsumerStatefulWidget
class HomePage extends ConsumerStatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  ConsumerState<HomePage> createState() => _HomePageState();
}

class _HomePageState extends ConsumerState<HomePage> {
  // 在 State 中建立並持有一個 Controller
  final CardSwiperController _controller = CardSwiperController();

  @override
  Widget build(BuildContext context) {
    // ... build 方法的內容會放在這裡
  }
}

二、將Controller綁定到Widget

在 build 方法中,找到CardSwiper Widget,並將我們剛剛建立的 _controller傳給它。

CardSwiper(
  controller: _controller, // 綁定 Controller
  // ... 其他參數
)

第二步:打造底部的按鈕列UI

接下來在CardSwiper的下方新增一排按鈕,使用Row和IconButton來快速實現這個佈局。

修改_HomePageState的build方法:

@override
Widget build(BuildContext context) {
  final insights = ref.watch(insightNotifierProvider);

  return Scaffold(
    appBar: AppBar(
      title: const Text('今日三則靈感'),
    ),
    // 使用 Column 將卡片和按鈕垂直排列
    body: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        // 卡片區域
        SizedBox(
          height: 500,
          width: 350,
          child: insights.isEmpty
              ? const Center(child: Text('今天沒有新靈感了!'))
              : CardSwiper(
                  controller: _controller,
                  // ... 省略其他 CardSwiper 參數
                ),
        ),
        const SizedBox(height: 30), // 增加一些間距
        // 按鈕區域
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            // 捨棄按鈕
            IconButton(
              icon: const Icon(Icons.close, color: Colors.redAccent),
              iconSize: 40,
              onPressed: () {
                 _controller.swipeLeft();
              },
            ),
            // 收藏按鈕
            IconButton(
              icon: const Icon(Icons.favorite, color: Colors.amber),
              iconSize: 40,
              onPressed: () {
                // 收藏邏輯...
              },
            ),
            // 保留按鈕
            IconButton(
              icon: const Icon(Icons.check, color: Colors.green),
              iconSize: 40,
              onPressed: () {
                _controller.swipeRight();
              },
            ),
          ],
        )
      ],
    ),
  );
}

第三步:連接「捨棄」與「保留」按鈕

這一步非常簡單。因為我們已經在Day 10的onSwipe回呼中處理了狀態更新的邏輯,現在我們只需要呼叫controller對應的方法,就能觸發動畫和onSwipe事件。

  • 捨棄按鈕: onPressed 中呼叫 _controller.swipeLeft();
  • 保留按鈕: onPressed 中呼叫 _controller.swipeRight();

第四步:作全新的「收藏」功能

「收藏」是一個新的業務邏輯。它不僅是將卡片滑掉,更要把這張卡片的資料存到另一個地方。為此,我們需要建立一個新的Provider來專門管理「收藏列表」。

一、建立FavoriteProvider

在lib/features/favorite/目錄下建立favorite_provider.dart

二、綁定收藏按鈕事件

回到 _HomePageState,修改收藏按鈕的onPressed

我們從insightNotifierProvider中取得最上面的卡片(insights.first),然後透過ref.read 呼叫 favoriteProvider的addFavorite 方法將它加入收藏列表。成功後,會顯示一個SnackBar作為即時反饋,並觸發向右滑動,流暢地進入下一張卡片。

註:分享按鈕的功能我們將在之後再介紹,以share_plus套件實作,現在可以先保留按鈕位置。

明日預告:建立「收藏頁面」

今天我們為App增加了更多的互動性,完成了使用CardSwiperController、將Widget升級為 ConsumerStatefulWidget、建立了按鈕列UI,並實作了全新的「收藏」功能。

再次實踐了Riverpod管理多個獨立狀態的優雅與簡潔,現在,使用者不僅能滑動,還能精準地點擊按鈕來表達他們的意圖。但是,收藏的靈感去哪了呢?我們還沒有地方可以查看它們。

這正是明天的任務!在Day 12將會建立一個「收藏頁面」,顯示所有被我們珍藏起來的靈感~這將會涉及到頁面導航以及如何讀取favoriteProvider的狀態。明天見啦!


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


上一篇
30 天做一個極簡App:狀態管理入門「Riverpod 管理卡片狀態」!
下一篇
30 天做一個極簡App:收藏頁面的誕生
系列文
Mobile Dev|日更靈感來源 App:Flutter × LLM × n8n,每天只推 3 則!12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言