下面源码时练习时做的,实现了购物车的单选,全选,总价计算,
需要定义数量和是否选中状态为响应式的数据。
CardListModel
import 'package:get/get.dart';
class CardListModel {int? id;RxInt? num; // 使用 RxInt 使 num 成为响应式的String? name;double? price;RxBool show; // 添加 RxBool 类型的 show 字段CardListModel({this.id,int? numValue, // 接收一个普通的 int,然后将其转换为 RxIntthis.name,this.price,bool showValue = false, // 默认为 false}) : num = RxInt(numValue ?? 0), show = RxBool(showValue);factory CardListModel.fromJson(Map<String, dynamic> json) => CardListModel(id: json['id'] as int?,numValue: json['num'] as int?, // 注意这里使用 numValue 而不是直接 numname: json['name'] as String?,price: json['price'] as double?,showValue: false, // JSON 数据中没有 show 字段,所以设置为 false);Map<String, dynamic> toJson() => {'id': id,'num': num!.value,'name': name,'price': price};
}
controller控制器
import 'package:flutter_aidishi/utils/loading.dart';
import 'package:get/get.dart';
import '../../../models/home/card_list.dart';class CardController extends GetxController {CardController();RxList<CardListModel> cardList = <CardListModel>[].obs;// 响应式数据List<String> selectIds = []; // 选中项的id数组bool isAllSelected = false; // 底部是否全选状态double total = 0.0; // 总价bool isEdit = false; // 编辑/完成_initData() {// 模拟接口返回的数据结构var data = [{'name':'生菜500g/份','price':100.5,'id':1,'num':1,},{'name':'云南产地直发生菜500g/份','price':300.0,'id':2,'num':1,},];for (var item in data) {cardList.add(CardListModel.fromJson(item));}update(["card"]);}// 单选void onSelect(bool selected,int index){var id = cardList[index].id.toString();var p = cardList[index].price as double;var num = cardList[index].num?.value;var itemTotal = (p*num!);if(selected){cardList[index].show.value = true;selectIds.add(id);total += itemTotal;if(selectIds.length == cardList.length){isAllSelected = true;}}else{cardList[index].show.value = false;selectIds.remove(id);total -= itemTotal;isAllSelected = false;}update(["card_foot"]);}// 全选void onSelectAll(bool selected){isAllSelected = selected;total = 0.0;selectIds.clear();if(selected){// 全选,把所有购车中的id取出来,放到selectIds中for(var i = 0; i <cardList.length;i++){var id = cardList[i].id.toString();selectIds.add(id);var p = cardList[i].price as double;var num = cardList[i].num?.value;var itemTotal = (p*num!);total += itemTotal;}}update(["card"]);}// 加减价格修改void onTapItemNum(index,value){cardList[index].num?.value = value;total = 0.0;for(var i = 0; i <cardList.length;i++){if(cardList[i].show.value){var p = cardList[i].price as double;var num = cardList[i].num?.value;var itemTotal = (p*num!);total += itemTotal;}}update(["card_foot"]);}// 编辑/完成切换void onTapEdit(){total = 0.0;isAllSelected = false;isEdit = !isEdit;update(["card"]);}// 删除void submit_del(){if(selectIds.length == 0){Loading.error('请先选择要删除的商品');}print('点击删除');}// 结算void submit_pay(){if(selectIds.length == 0){Loading.error('请先选择要结算的商品');}print('点击下单');}@overridevoid onInit() {super.onInit();}@overridevoid onReady() {super.onReady();_initData();}@overridevoid onClose() {super.onClose();}
}
主视图
import 'package:flutter/material.dart';
import 'package:flutter_aidishi/extension/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../../widget/index.dart';
import 'index.dart';
import 'widgets/cardFoot.dart';class CardPage extends GetView<CardController> {const CardPage({super.key});// 购物车订单Widget _buildCardList(){// 分隔列表return ListView.separated(// 每一个item项itemBuilder: (BuildContext context,int index){return TDCheckboxGroupContainer(selectIds: controller.isAllSelected ? controller.selectIds : [],child: <Widget>[// 复选框TDCheckbox(id: controller.cardList[index].id.toString(),style: TDCheckboxStyle.circle,showDivider: false,onCheckBoxChanged: (selected){controller.onSelect(selected,index);},),SizedBox(width: 8.w,),// 商品图Container(width: 80.w,height: 80.w,decoration: BoxDecoration(borderRadius: BorderRadius.circular(10.w),image: DecorationImage(image: AssetImage('assets/images/goods.jpg'))),),SizedBox(width: 10.w,),<Widget>[Text('${controller.cardList[index].name}',maxLines: 1,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 14.sp,fontWeight: FontWeight.bold),),SizedBox(height: 5.w,),<Widget>[Text('¥',style: TextStyle(fontSize: 11.sp,color: Color(0xffFF770F)),),Text('${controller.cardList[index].price}',style: TextStyle(fontSize: 17.sp,color: Color(0xffFF770F),fontWeight: FontWeight.bold),)].toRow(),SizedBox(height: 5.w,),<Widget>[// 步进器TDStepper(theme: TDStepperTheme.filled,value:controller.cardList[index].num?.value,min:1,onChange:(value){controller.onTapItemNum(index,value);})].toRow(mainAxisAlignment: MainAxisAlignment.end)].toColumn(crossAxisAlignment: CrossAxisAlignment.start).expanded()].toRow().paddingOnly(top: 15.w,right: 15.w,bottom: 15.w,left: 15.w).card(radius: 10.w,color: Colors.white).marginOnly(left: 15.w,right: 15.w));},// 定义每个item之间的分隔距离separatorBuilder: (BuildContext context,int index){return SizedBox(height: 10.w,);},// 一共有多少条记录itemCount: controller.cardList.length,);}// 主视图Widget _buildView() {return <Widget>[SizedBox(height: 10.w,),_buildCardList().expanded(),CardFootPage(), // 底部单独抽成组件更新全选总价的状态].toColumn();}@overrideWidget build(BuildContext context) {return GetBuilder<CardController>(init: CardController(),id: "card",builder: (_) {return Scaffold(appBar: AppBar(title: Text("购物车(${controller.cardList.length})"),actions: [GestureDetector(onTap: (){controller.onTapEdit();},child: Container(margin: EdgeInsets.only(right: 15.w),child: Text(controller.isEdit ? '完成' : '编辑',style: TextStyle(fontSize: 14.sp,fontWeight: FontWeight.w500),),),)],),body: SafeArea(child: _buildView(),),backgroundColor: AppColors.ColorPageBg,);},);}
}
CardFootPage
import 'package:flutter/material.dart';
import 'package:flutter_aidishi/extension/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../../../widget/index.dart';
import '../index.dart';class CardFootPage extends GetView<CardController> {const CardFootPage({super.key});// 底部Widget _buildFooter(){return <Widget>[<Widget>[TDCheckboxGroupContainer(selectIds: controller.isAllSelected ? ['1'] : [],child: TDCheckbox(id: '1',style: TDCheckboxStyle.circle,showDivider: false,onCheckBoxChanged: (bool selected){controller.onSelectAll(selected);},)),SizedBox(width: 10.w,),Text('合计',style: TextStyle(fontSize: 14.sp,color: AppColors.Color999),),Text('¥',style: TextStyle(fontSize: 11.sp,color: Color(0xffFF770F)),),Text('${controller.total}',style: TextStyle(fontSize: 17.sp,color: Color(0xffFF770F),fontWeight: FontWeight.bold),)].toRow().expanded(),controller.isEdit ? TDButton(text: '删除',height: 52.w,width:100.w,iconWidget: TDLoading(size: TDLoadingSize.small,icon: TDLoadingIcon.circle,iconColor: AppColors.mainColor,),size: TDButtonSize.large,type: TDButtonType.fill,shape: TDButtonShape.filled,theme: TDButtonTheme.danger,onTap: controller.submit_del,) : TDButton(text: '结算',height: 52.w,width:100.w,iconWidget: TDLoading(size: TDLoadingSize.small,icon: TDLoadingIcon.circle,iconColor: AppColors.mainColor,),size: TDButtonSize.large,type: TDButtonType.fill,shape: TDButtonShape.filled,theme: TDButtonTheme.primary,onTap: controller.submit_pay,)].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween).paddingOnly(left: 15.w).decorated(color: AppColors.Colorfff.withOpacity(1),).boxShadow(color: AppColors.Color999.withOpacity(0.5),blurRadius:8,spreadRadius:1).height(52.w);}// 主视图Widget _buildView() {return <Widget>[_buildFooter(),].toColumn();}@overrideWidget build(BuildContext context) {return GetBuilder<CardController>(init: CardController(),id: "card_foot",builder: (_) {return _buildView();},);}
}