17.1 商品页加购物车

17.1 商品页加购物车

img

实现步骤:


第 1 步:购物车数据结构

img
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"line_items": [
{
"id": 33,
"name": "Beanie",
"product_id": 15,
"variation_id": 0,
"quantity": 2,
"tax_class": "",
"subtotal": "18.00",
"subtotal_tax": "0.00",
"total": "18.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": [],
"sku": "woo-beanie",
"price": 9,
"parent_name": null,
"product": {
"id": 15,
"name": "Beanie",
"slug": "beanie",
"permalink": "https://wp.ducafecat.tech/product/beanie/",
"date_created": "2022-03-31T23:19:37",
"date_created_gmt": "2022-03-31T15:19:37",
"date_modified": "2022-04-18T23:50:20",
"date_modified_gmt": "2022-04-18T15:50:20",
"type": "simple",
"status": "publish",
"featured": true,
"catalog_visibility": "visible",
"description": "<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>\n",
"short_description": "<p>This is a simple product.</p>\n",
"sku": "woo-beanie",
"price": "18",
"regular_price": "20",
"sale_price": "18",
"date_on_sale_from": null,
"date_on_sale_from_gmt": null,
"date_on_sale_to": null,
"date_on_sale_to_gmt": null,
"on_sale": true,
"purchasable": true,
"total_sales": 0,
"virtual": false,
"downloadable": false,
"downloads": [],
"download_limit": 0,
"download_expiry": 0,
"external_url": "",
"button_text": "",
"tax_status": "taxable",
"tax_class": "",
"manage_stock": false,
"stock_quantity": null,
"backorders": "no",
"backorders_allowed": false,
"backordered": false,
"low_stock_amount": null,
"sold_individually": false,
"weight": "1",
"dimensions": {
"length": "12",
"width": "12",
"height": "3"
},
"shipping_required": true,
"shipping_taxable": true,
"shipping_class": "china_shipping",
"shipping_class_id": 41,
"reviews_allowed": true,
"average_rating": "0.00",
"rating_count": 0,
"upsell_ids": [],
"cross_sell_ids": [],
"parent_id": 0,
"purchase_note": "",
"categories": [
{
"id": 29,
"name": "Man",
"slug": "man"
},
{
"id": 30,
"name": "Woman",
"slug": "woman"
},
{
"id": 34,
"name": "Bag",
"slug": "bag"
},
{
"id": 15,
"name": "More",
"slug": "more"
}
],
"tags": [
{
"id": 42,
"name": "beanie",
"slug": "beanie"
}
],
"images": [
{
"id": 44,
"date_created": "2022-04-01T07:20:01",
"date_created_gmt": "2022-03-31T15:20:01",
"date_modified": "2022-04-01T07:20:01",
"date_modified_gmt": "2022-03-31T15:20:01",
"src": "https://ducafecat.oss-cn-beijing.aliyuncs.com/wp-content/uploads/2022/03/beanie-2.jpg",
"name": "beanie-2.jpg",
"alt": ""
}
],
"attributes": [
{
"id": 1,
"name": "Color",
"position": 0,
"visible": true,
"variation": false,
"options": [
"Green",
"Purple"
]
},
{
"id": 2,
"name": "Size",
"position": 1,
"visible": true,
"variation": false,
"options": [
"XL",
"2XL",
"3XL"
]
}
],
"default_attributes": [],
"variations": [],
"grouped_products": [],
"menu_order": 0,
"price_html": "<del aria-hidden=\"true\"><span class=\"woocommerce-Price-amount amount\"><bdi><span class=\"woocommerce-Price-currencySymbol\">&yen;</span>20.00</bdi></span></del> <ins><span class=\"woocommerce-Price-amount amount\"><bdi><span class=\"woocommerce-Price-currencySymbol\">&yen;</span>18.00</bdi></span></ins>",
"related_ids": [
12,
11,
13,
14
],
"meta_data": [
{
"id": 89,
"key": "_original_id",
"value": "48"
},
{
"id": 479,
"key": "_wpcom_is_markdown",
"value": "1"
}
],
"stock_status": "instock",
"has_options": false,
"_links": {
"self": [
{
"href": "https://wp.ducafecat.tech/wp-json/wc/v3/products/15"
}
],
"collection": [
{
"href": "https://wp.ducafecat.tech/wp-json/wc/v3/products"
}
]
}
}
}
],

第 2 步:统一产品数据模型

lib/common/models/woo/order_model/line_item.dart

1
2
3
4
5
6
import '../../index.dart';

class LineItem {
...
ProductModel? product;

第 3 步:编写 CartService

lib/common/services/cart.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import 'package:get/get.dart';

import '../index.dart';

/// 购物车服务
class CartService extends GetxService {
static CartService get to => Get.find();

/// 购物车商品
final List<LineItem> lineItems = RxList<LineItem>();

/// 加入商品
void addCart(LineItem item) {
// 检查是否存在
int index =
lineItems.indexWhere((element) => element.productId == item.productId);
if (index >= 0) {
// 存在,更新数量
item = lineItems.elementAt(index);
item.quantity = item.quantity! + 1;
item.price = double.parse(item.product!.price ?? "0");
item.total = '${item.price! * item.quantity!}';
} else {
// 不存在,添加
item.quantity = 1;
item.price = double.parse(item.product!.price ?? "0");
item.total = '${item.price! * item.quantity!}';
lineItems.add(item);
}
}

/// 删除商品
void cancelOrder(int productId) {
lineItems.removeWhere((element) => element.productId == productId);
}

/// 修改商品数量
void changeQuantity(int productId, int quantity) {
// 小于等于 0 删除
if (quantity <= 0) {
cancelOrder(productId);
return;
}

// 设置商品数量
LineItem item =
lineItems.firstWhere((element) => element.productId == productId);
item.quantity = quantity;
item.price = double.parse(item.product!.price ?? "0");
item.total = '${item.price! * item.quantity!}';
}

/// 清空购物车
void clear() {
lineItems.clear();
}

/// 商品数量
int get lineItemsCount => lineItems.length;

/// 运费
double get shipping => 0;

/// 折扣
double get discount => 0;

/// 商品合计价格
double get totalItemsPrice =>
lineItems.fold<double>(0, (double previousValue, LineItem element) {
return previousValue + double.parse(element.total ?? "0");
});
}

载入 CartService

lib/global.dart

1
2
3
4
5
class Global {
static Future<void> init() async {
...
Get.put<CartService>(CartService());
}

第 4 步:加入购物车按钮

lib/pages/goods/product_details/controller.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 加入购物车
void onAddCartTap() async {
// 检查是否登录
if (!await UserService.to.checkIsLogin()) {
return;
}

// 检查空
if (product == null || product?.id == null) {
Loading.error("product is empty");
return;
}

// 加入购物车
CartService.to.addCart(LineItem(
productId: productId,
product: product,
));
// 返回、或者去购物车
Get.back();
}

加完购物车 退出商品商品详情页

lib/pages/goods/product_details/view.dart

1
2
3
4
5
6
7
8
9
// 底部按钮
Widget _buildButtons() {
return <Widget>[
// 加入购物车
ButtonWidget.secondary(
LocaleKeys.gDetailBtnAddCart.tr,
onTap: controller.onAddCartTap, // 加入购物车事件
).expanded(),
...

第 5 步:底部导航更新购物车数量

lib/pages/system/main/view.dart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 主视图
Widget _buildView() {
...
// 导航栏
bottomNavigationBar: GetBuilder<MainController>(
id: 'navigation',
builder: (controller) {
// obx 放在 BuildNavigation 位置
return Obx(() => BuildNavigation(
currentIndex: controller.currentIndex,
items: [
...
NavigationItemModel(
label: LocaleKeys.tabBarCart.tr,
icon: AssetsSvgs.navCartSvg,
// 购物车数量
count: CartService.to.lineItemsCount,
),
...
],
onTap: controller.onJumpToPage, // 切换tab事件
));
},
),

Obx 方式是 Getx 的全局订阅响应

提交代码到 git