日安(換個方式打招呼)!經過Day 10的努力,App已經脫胎換骨。Riverpod 的加持,每一次滑動不再是過眼雲煙,而是會真實地改變App狀態的有效操作。
滑動操作雖然流暢直觀,但優秀的UX設計通常會為核心功能提供多種入口。有些使用者可能更喜歡明確的點擊按鈕。因此今天的任務是根據UI Wireframe的規劃,在卡片下方加入一排互動按鈕:「捨棄」、「收藏」、和「保留」。
我們將會學習如何透過Controller來命令卡片進行滑動,並實作全新的「收藏」功能,讓使用者可以將真正有價值的靈感永久保存下來。
第一步:用CardSwiperController控制你的卡片
要實現點擊按鈕來觸發卡片滑動,最優雅的方式就是使用flutter_card_swiper套件提供的 CardSwiperController。這個控制器可以讓我們從外部程式碼(例如按鈕的onPressed事件)去命令卡片疊執行swipeLeft、swipeRight等動作。
Controller是一個有狀態的物件,需要被初始化和持有,所以需要將昨天的HomePage從ConsumerWidget升級為ConsumerStatefulWidget。
// 將 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 方法的內容會放在這裡
}
}
在 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事件。
第四步:作全新的「收藏」功能
「收藏」是一個新的業務邏輯。它不僅是將卡片滑掉,更要把這張卡片的資料存到另一個地方。為此,我們需要建立一個新的Provider來專門管理「收藏列表」。
在lib/features/favorite/目錄下建立favorite_provider.dart
回到 _HomePageState,修改收藏按鈕的onPressed
我們從insightNotifierProvider中取得最上面的卡片(insights.first),然後透過ref.read 呼叫 favoriteProvider的addFavorite 方法將它加入收藏列表。成功後,會顯示一個SnackBar作為即時反饋,並觸發向右滑動,流暢地進入下一張卡片。
今天我們為App增加了更多的互動性,完成了使用CardSwiperController、將Widget升級為 ConsumerStatefulWidget、建立了按鈕列UI,並實作了全新的「收藏」功能。
再次實踐了Riverpod管理多個獨立狀態的優雅與簡潔,現在,使用者不僅能滑動,還能精準地點擊按鈕來表達他們的意圖。但是,收藏的靈感去哪了呢?我們還沒有地方可以查看它們。
這正是明天的任務!在Day 12將會建立一個「收藏頁面」,顯示所有被我們珍藏起來的靈感~這將會涉及到頁面導航以及如何讀取favoriteProvider的狀態。明天見啦!
【哈囉你好:)感謝你的閱讀!其他我會常出沒的地方:Threads】