承接上篇文章 D-20 Provider 購物車實作(上) | Flutter筆記
介紹購物車設計
購物車CartModel實體,存在於多個畫面
否則目錄頁加一件商品,卻不會在購物車裡面
import 'dart:collection';
import 'package:flutter/widgets.dart';
import 'package:flutter_account_note/shop/model/catalog.dart';
class CartModel extends ChangeNotifier {
final List<int> _itemIds = []; //加購的商品id存放
late CatalogModel _catalog; //私有目錄 僅能透過以下get,set 讀取和寫入
//1. 取得目錄內容
CatalogModel get catalog => _catalog;
//2. 設定目錄內容
set catalog(CatalogModel newCatalog) {
_catalog = newCatalog;
notifyListeners();
}
//---------------------
//_itemIds是選購的商品
//map將id快速轉換成List<item>
//白話: 購物車商品列表
List<Item> get items => _itemIds.map((id) => _catalog.getById(id)).toList();
//總價格計算 fold會將list內容加總 , 0代表初始值
int get totalPrice => items.fold(0, (total, current) => total + current.price);
//------------------
// add removeAll
// 要對購物車操作 新增商品 移除全部
// 所以皆需要notifyListeners 告訴我們畫面要更新了
void add(Item item) {
_itemIds.add(item.id);
notifyListeners();
}
void removeAll() {
_itemIds.clear();
notifyListeners();
}
void remove(Item item) {
_itemIds.remove(item.id);
notifyListeners();
}
}
import 'package:flutter/material.dart';
import 'package:flutter_account_note/shop/model/cart.dart';
import 'package:flutter_account_note/shop/model/catalog.dart';
import 'package:provider/provider.dart';
class Catalog extends StatelessWidget {
const Catalog({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: [
_appBar(),
const SliverToBoxAdapter(
child: SizedBox(height: 12),
),
//產生商品列表 單一個商品由_listItem產生
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => _listItem(index),
childCount: 4,
),
),
],
),
);
}
}
class _appBar extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SliverAppBar(
title: Text(
'Catalog',
style: Theme.of(context).textTheme.displayMedium,
),
actions: [
IconButton(
onPressed: () => Navigator.pushNamed(context, '/cart'),
icon: const Icon(Icons.shopping_cart),
)
],
);
}
}
class _listItem extends StatelessWidget {
final int index;
const _listItem(this.index);
@override
Widget build(BuildContext context) {
//讀取CatalogModel中的商品目錄index 代表得到目錄中第幾個
var item = context.select<CatalogModel, Item>(
(catalog) => catalog.getByPosition(index),
);
var textTheme = Theme.of(context).textTheme.titleLarge;
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: LimitedBox(
maxHeight: 48,
child: Row(
children: [
AspectRatio(
aspectRatio: 1,
child: Container(
color: item.color,
),
),
const SizedBox(width: 24),
Expanded(
child: Text(item.name, style: textTheme),
),
const SizedBox(width: 24),
_addButton(item: item),
],
),
),
);
}
}
//商品按鈕 點擊後加入購物車
class _addButton extends StatelessWidget {
const _addButton({Key? key, required this.item}) : super(key: key);
final Item item;
@override
Widget build(BuildContext context) {
//檢查商品(Item)是否在購物車(Cart)中
var isInCart = context.select<CartModel, bool>((cart) => cart.items.contains(item));
return TextButton(
onPressed: isInCart
? null
: () {
//點擊後 , 條件判斷沒有在購物車的話 ,即加入購物車
var cart = context.read<CartModel>();
cart.add(item);
},
child: isInCart ? const Icon(Icons.check, semanticLabel: 'ADDED') : const Text('ADD'),
style: ButtonStyle(
overlayColor: MaterialStateProperty.resolveWith<Color?>((states) {
if (states.contains(MaterialState.pressed)) {
return Theme.of(context).primaryColor;
}
return null;
}),
),
);
}
}