Flutter實(shí)現(xiàn)購(gòu)物車(chē)功能(代碼+邏輯)
一、初始化時(shí)判斷是否為登錄狀態(tài)
假設(shè)是登錄狀態(tài)從本地中取出token,帶參傳遞給后端請(qǐng)求登錄后購(gòu)物車(chē)數(shù)據(jù)




二、分析點(diǎn)擊全選和非全選狀態(tài)
1.全選狀態(tài)就是把數(shù)組中的數(shù)據(jù)對(duì)應(yīng)的每個(gè)check值設(shè)置為true,總價(jià)就是后端返回的全選數(shù)量的總價(jià)格


設(shè)置全選和非全選

這個(gè)的改變是由初始化查詢(xún)數(shù)組列表中是否為全選或非全選狀態(tài)所決定,如果遇見(jiàn)一個(gè)check為false那么就返回false
2.非全選狀態(tài)(總價(jià)結(jié)果為0或者當(dāng)前選中對(duì)象的總價(jià)之和)
判斷是否非全選狀態(tài)


三、改變單個(gè)物品check
觸發(fā)checkCart接口進(jìn)行更新全選狀態(tài)的總價(jià),以及當(dāng)前選中商品總價(jià)



四、點(diǎn)擊數(shù)量增加和減少按鈕改變當(dāng)前顯示number值
觸發(fā)update接口等更新數(shù)量,觸發(fā)查詢(xún)接口更改選中商品總價(jià),以及全選總價(jià)


自定義的數(shù)量組件返回的value是當(dāng)前增加或減少改變后的number值



五、長(zhǎng)按刪除數(shù)據(jù)
調(diào)用刪除接口重新渲染頁(yè)面

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:myshop_flutter/components/gradient_button.dart';
import 'package:myshop_flutter/config/colours.dart';
import 'package:myshop_flutter/config/index.dart';
import 'package:myshop_flutter/event/cart_number_event.dart';
import 'package:myshop_flutter/event/category_event.dart';
import 'package:myshop_flutter/event/login_event.dart';
import 'package:myshop_flutter/event/refresh_event.dart';
import 'package:myshop_flutter/model/cart_list_model.dart';
import 'package:myshop_flutter/page/CategoryPage/GoodCategory/GooddetailWidget/cart_number_widget.dart';
import 'package:myshop_flutter/service/cart_service.dart';
import 'package:myshop_flutter/utils/navigator_util.dart';
import 'package:myshop_flutter/utils/shared_preferences_util.dart';
import 'package:myshop_flutter/utils/toast_util.dart';
import 'package:myshop_flutter/widgets/cached_image_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CartPage extends StatefulWidget {
const CartPage({Key key}) : super(key: key);
@override
_CartPageState createState() => _CartPageState();
}
class _CartPageState extends State<CartPage> {
//購(gòu)物車(chē)數(shù)據(jù)服務(wù)
CartService _cartService = CartService();
//購(gòu)物車(chē)列表數(shù)據(jù)
List<CartModel> _cartList;
//購(gòu)物車(chē)列表數(shù)據(jù)模型
CartListModel _cartListModel;
//是否登錄
bool _isLogin = false;
//是否全選
bool _isAllCheck = false;
//是否全都不選
bool _isAllNotCheck = false;
//總計(jì)
double _totalMoney;
//token
var token;
//獲取當(dāng)前查詢(xún)購(gòu)物車(chē)中選中的商品數(shù)量
var _checkedGoodsAmount;
@override
void initState() {
super.initState();
//從緩存中取出當(dāng)前登錄狀態(tài)
SharedPreferencesUtil.getLoginSave().then((v){
print("登錄購(gòu)物車(chē)頁(yè)面獲取本地登錄值${v}");
if(v){
setState(() {
_isLogin = v;
});
//刷新購(gòu)物車(chē)數(shù)據(jù)
//從緩存中取出當(dāng)前的token值
SharedPreferencesUtil.getToken().then((onValue) {
if(onValue!=null){
_getCartData(onValue);
setState(() {
token = onValue;
});
}
});
}
});
}
_listener(){
//初始化的時(shí)候監(jiān)聽(tīng)是否登錄
loginEventBus.on<LoginEvent>().listen((LoginEvent loginEvent) {
print("購(gòu)物車(chē)頁(yè)面是否判定登錄");
setState(() {
_isLogin = loginEvent.isLogin;
});
});
}
//獲取購(gòu)物車(chē)數(shù)據(jù)
_getCartData(token) {
print("---------${token}");
//將token值放入請(qǐng)求頭里
Options options = Options(headers:{"X-Shop-Token" : token});
//查詢(xún)購(gòu)物車(chē)數(shù)據(jù)
_cartService.queryCart((cartList) {
setState(() {
_cartListModel = cartList;
_cartList = _cartListModel.cartList;
_checkedGoodsAmount = _cartListModel.cartTotal.checkedGoodsAmount;
_totalMoney = _cartListModel.cartTotal.goodsAmount;
});
//是否全選
_isAllCheck = _isCheckedAll();
_isAllNotCheck = _isNotCheckedAll();
}, (error) {
ToastUtil.showToast(error);
},options:options);
}
//判斷是否全部不選,如果有一個(gè)等于true就返回false
_isNotCheckedAll(){
//迭代循環(huán)購(gòu)物車(chē)列表所有checked屬性,當(dāng)全部為true時(shí)為全選狀態(tài)
for (int i = 0; i < _cartList.length; i++) {
if (_cartList[i].checked == null || _cartList[i].checked ) {
return false;
}
}
return true;
}
// //判斷是否全選
bool _isCheckedAll() {
//迭代循環(huán)購(gòu)物車(chē)列表所有checked屬性,當(dāng)全部為true時(shí)為全選狀態(tài)
for (int i = 0; i < _cartList.length; i++) {
if (_cartList[i].checked == null || !_cartList[i].checked) {
return false;
}
}
return true;
}
//監(jiān)聽(tīng)刷新事件,當(dāng)用戶(hù)從商品詳情頁(yè)面點(diǎn)擊添加至購(gòu)物車(chē)時(shí)會(huì)觸發(fā)刷新事件
_refreshEvent() {
//重新調(diào)用接口賦值
CarteventBus.on<RefreshEvent>().listen((RefreshEvent refreshEvent){
if(refreshEvent.isRefresh){
_getCartData(token);
}
});
CarteventBus.fire(RefreshEvent(
false
));
}
@override
Widget build(BuildContext context) {
//監(jiān)聽(tīng)刷新事件
_listener();
_refreshEvent();
return _isLogin == true
? Scaffold(
appBar: AppBar(
centerTitle: true,
title: Text("購(gòu)物車(chē)"),
),
body: _cartList.length == 0?
Container(
padding: EdgeInsets.only(top: 70),
child: Center(
child: Column(
children: [
Image.asset(
"images/emptycar.png",
width: 135,
height: 135,
),
SizedBox(
height: 46,
),
Text(
"還沒(méi)有添加任何商品,快去選購(gòu)吧!",
style: TextStyle(
fontSize: 15, color: Colours.textBlack32),
),
SizedBox(
height: 80,
),
GestureDetector(
onTap: () {
// Navigator.pop(context);
},
child: Container(
alignment: Alignment.center,
width: 100,
height: 40,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colours.directBB1,
Colours.directBB2,
],
end: Alignment.bottomCenter,
begin: Alignment.topCenter,
),
borderRadius: BorderRadius.circular(150),
),
child: Text(
"去逛逛",
style: TextStyle(
color: Colors.white, fontSize: 16),
),
),
),
],
),
),
)
: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
//渲染購(gòu)物車(chē)列表數(shù)據(jù)
ListView.builder(
//購(gòu)物車(chē)列表項(xiàng)個(gè)數(shù)
itemCount: _cartList.length,
//購(gòu)物車(chē)列表項(xiàng)構(gòu)建器
itemBuilder: (BuildContext context, int index) {
//根據(jù)索引返回列表項(xiàng)
return _getCartItemWidget(index);
}),
Container(
height: 60,
decoration:BoxDecoration(
color: Colors.white,
),
//水平布局
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 3,
child: Row(
children: [
//全選復(fù)選框
Checkbox(
value: _isAllCheck,
activeColor: KColor.defaultCheckBoxColor,
//選擇改變事件回調(diào)
onChanged: (bool) {
//設(shè)置是否全選
_setCheckedAll(bool);
}),
Text("全選"),
Expanded(
child: Container(
alignment: Alignment.centerRight,
margin: EdgeInsets.only(right: 20),
//全選價(jià)格
child: Text(_isAllCheck
? KString.TOTAL_MONEY +"${_totalMoney}"
: _isNotCheckedAll == true ? KString.TOTAL_MONEY +"0.0":KString.TOTAL_MONEY + "${_checkedGoodsAmount}"),
)),
],
)),
Expanded(
flex: 1,
child: Container(
margin: EdgeInsets.only(
right:
30,
),
alignment: Alignment.centerRight,
//結(jié)算按鈕
child: RaisedButton(
//結(jié)算操作
onPressed: () {
//跳轉(zhuǎn)到填寫(xiě)訂單頁(yè)面
_fillInOrder();
},
color: KColor.defaultButtonColor,
child: Text(
//結(jié)算標(biāo)簽
KString.SETTLEMENT,
style: TextStyle(
color: Colors.white,
fontSize:17),
),
),
),
),
],
),
)
],
))
: Scaffold(
appBar: AppBar(
elevation: 0,
centerTitle: true,
title: Text(
"購(gòu)物車(chē)",
style: TextStyle(color: Colors.white),
),
backgroundColor: Colors.lightBlueAccent,
),
body: Container(
child: Column(
children: [
SizedBox(
height: 40,
),
Container(
alignment: Alignment.center,
child: Image.asset(
"images/wukong.png",
height: 60,
width: double.infinity,
),
),
Center(
child: Text("還沒(méi)有登錄哦"),
),
GestureDetector(
onTap: () {},
child: Padding(
padding: EdgeInsets.fromLTRB(30, 10, 30, 0),
child:
GradientButton("去登錄", 0xFFFF9E00, 0xFFFF4800, () {
NavigatorUtil.goLogin(context);
}, textSize: 18, textColor: 0xFFFEFEFE),
))
],
),
),
);
}
//跳轉(zhuǎn)至填寫(xiě)訂單頁(yè)面
_fillInOrder() {
NavigatorUtil.goFillInOrder(context, 0);
}
//設(shè)置是否全選/全不選
_setCheckedAll(bool checked) {
setState(() {
_isAllCheck = checked;
for (int i = 0; i < _cartList.length; i++) {
_cartList[i].checked = checked;
}
});
}
//刪除對(duì)話(huà)框
_deleteDialog(int index) {
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return AlertDialog(
//提示
title: Text(KString.TIPS),
//是否確認(rèn)刪除
content: Text(KString.DELETE_CART_ITEM_TIPS),
actions: <Widget>[
//取消按鈕
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(
KString.CANCEL,
style: TextStyle(color: Colors.black54),
),
),
//刪除按鈕
FlatButton(
onPressed: () {
//刪除商品
_deleteGoods(index);
},
child: Text(
KString.CONFIRM,
style: TextStyle(color: KColor.defaultTextColor),
),
)
],
);
});
}
//根據(jù)索引刪除購(gòu)物車(chē)商品
_deleteGoods(int index) {
//獲取token值
Options options = Options(headers:{"X-Shop-Token" : token});
//通過(guò)索引獲取到產(chǎn)品Id
var parameters = {
"productIds": [_cartList[index].productId]
};
//調(diào)用刪除商品方法
_cartService.deleteCart((success) {
//刪除成功提示
ToastUtil.showToast(KString.DELETE_SUCCESS);
setState(() {
//本地列表移除數(shù)據(jù)
_cartList.removeAt(index);
});
Navigator.pop(context);
}, (error) {
ToastUtil.showToast(error);
}, parameters,options:options);
}
//根據(jù)索引獲取購(gòu)物車(chē)項(xiàng)組件
Widget _getCartItemWidget(int index) {
return Container(
height:130,
width: double.infinity,
child: InkWell(
//長(zhǎng)按打開(kāi)刪除商品對(duì)話(huà)框
onLongPress: () => _deleteDialog(index),
child: Card(
//水平布局
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
//是否勾選此商品
Checkbox(
//讀取購(gòu)物車(chē)列表數(shù)據(jù)中當(dāng)前項(xiàng)的checked值
value: _cartList[index].checked ?? true,
activeColor: KColor.defaultCheckBoxColor,
//改變回調(diào)方法
onChanged: (bool) {
_checkCart(index, bool);
}),
//緩存商品圖片
CachedImageWidget(
80,
80,
//商品圖片路徑
_cartList[index].picUrl,
),
//垂直布局
SizedBox(width: 30,),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//商品名稱(chēng)
Text(
_cartList[index].goodsName,
style: TextStyle(
fontSize:16,
color: Colors.black54),
),
Padding(
padding: EdgeInsets.only(
top: 10,
),
),
//商品價(jià)格
Text(
"¥${_cartList[index].price}",
style: TextStyle(
fontSize: 16,
color: Colors.grey),
)
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//購(gòu)買(mǎi)商品數(shù)量
Text(
"X${_cartList[index].number}",
style: TextStyle(
color: Colors.black54,
fontSize: 16),
),
Padding(
padding: EdgeInsets.only(
top:10,
),
),
//使用購(gòu)物數(shù)量組件
CartNumberWidget(_cartList[index].number, (value) {
//根據(jù)返回的索引及數(shù)量更新購(gòu)物車(chē)
_updateCart(index, value);
}),
],
))
],
),
),
),
);
}
//是否勾選商品,傳入索引及是否勾選
_checkCart(int index, bool isCheck) {
Options options = Options(headers:{"X-Shop-Token" : token});
var parameters = {
//產(chǎn)品Id
"productIds": [_cartList[index].productId],
//是否選擇
"isChecked": isCheck ? 1 : 0,
};
//調(diào)用購(gòu)物車(chē)數(shù)據(jù)服務(wù)方法
_cartService.cartCheck((success) {
setState(() {
_cartListModel = success;
_cartList = _cartListModel.cartList;
//重新設(shè)置全選狀態(tài)
_isAllCheck = _isCheckedAll();
//計(jì)算總價(jià)
_checkedGoodsAmount = _cartListModel.cartTotal.checkedGoodsAmount;
_totalMoney = _cartListModel.cartTotal.goodsAmount;
});
}, (error) {
ToastUtil.showToast(error);
}, parameters,options);
}
//更新購(gòu)物車(chē),傳入索引及數(shù)量
_updateCart(int index, int number) {
Options options = Options(headers:{"X-Shop-Token" : token});
var parameters = {
//規(guī)格Id
"productId": _cartList[index].productId,
//商品Id
"goodsId": _cartList[index].goodsId,
//商品數(shù)量
"number": number,
//id
"id": _cartList[index].id,
};
_cartService.updateCart((success) {
setState(() {
_cartList[index].number = number;
});
_getCartData(token);
}, (error) {
ToastUtil.showToast(error);
cartNumberEventBus.fire(CartNumberEvent(number - 1));
}, options, parameters);
}
}
到此這篇關(guān)于Flutter實(shí)現(xiàn)購(gòu)物車(chē)功能(代碼+邏輯)的文章就介紹到這了,更多相關(guān)Flutter 購(gòu)物車(chē)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Flutter實(shí)現(xiàn)頁(yè)面切換后保持原頁(yè)面狀態(tài)的3種方法
- Flutter 超實(shí)用簡(jiǎn)單菜單彈出框 PopupMenuButton功能
- Flutter中http請(qǐng)求抓包的完美解決方案
- flutter InkWell實(shí)現(xiàn)水波紋點(diǎn)擊效果
- Flutter中如何加載并預(yù)覽本地的html文件的方法
- flutter日期選擇器 flutter時(shí)間選擇器
- Flutter中如何實(shí)現(xiàn)無(wú)Context跳轉(zhuǎn)詳解
- flutter Toast實(shí)現(xiàn)消息提示框
相關(guān)文章
Android自定義圓環(huán)倒計(jì)時(shí)控件
這篇文章主要為大家詳細(xì)介紹了Android自定義圓環(huán)倒計(jì)時(shí)控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09
Android Native 內(nèi)存泄漏系統(tǒng)化解決方案
這篇文章主要介紹了Android Native 內(nèi)存泄漏系統(tǒng)化解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
Android開(kāi)發(fā)之sqlite3命令行簡(jiǎn)單使用方法
這篇文章主要介紹了Android開(kāi)發(fā)之sqlite3命令行簡(jiǎn)單使用方法,分析了Android增刪改查等常用sqlite3的數(shù)據(jù)庫(kù)操作命令使用方法,需要的朋友可以參考下2016-02-02
Android開(kāi)發(fā)基礎(chǔ)之創(chuàng)建啟動(dòng)界面Splash Screen的方法
這篇文章主要介紹了Android開(kāi)發(fā)基礎(chǔ)之創(chuàng)建啟動(dòng)界面Splash Screen的方法,以實(shí)例形式較為詳細(xì)的分析了Android定制啟動(dòng)界面的布局及功能實(shí)現(xiàn)相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10
Android實(shí)現(xiàn)氣泡動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)氣泡動(dòng)畫(huà),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-04-04

