Flutter:购物车总结(单选,全选,定义数量,是否选中为obs响应式)

下面源码时练习时做的,实现了购物车的单选,全选,总价计算,

需要定义数量和是否选中状态为响应式的数据。

CardListModel

js 复制代码
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,然后将其转换为 RxInt
		this.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 而不是直接 num
		name: 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控制器

js 复制代码
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('点击下单');
  }


  @override
  void onInit() {
    super.onInit();
  }

  @override
  void onReady() {
    super.onReady();
    _initData();
  }

  @override
  void onClose() {
    super.onClose();
  }
}

主视图

js 复制代码
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();
  }

  @override
  Widget 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

js 复制代码
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();
  }

  @override
  Widget build(BuildContext context) {
    return GetBuilder<CardController>(
      init: CardController(),
      id: "card_foot",
      builder: (_) {
        return _buildView();
      },
    );
  }
}
相关推荐
Wuxiaoming1358 小时前
关于flutter与django建立mqtt的研究
flutter
Cao_Shixin攻城狮12 小时前
[Flutter]Json序列化json_serializable使用属性全介绍
flutter·json·serializable
五味香1 天前
Java学习,反射
android·java·开发语言·python·学习·flutter·kotlin
收银系统源码那点事1 天前
零售餐饮收银台源码
flutter·uni-app·零售·收银系统源码·收银系统·连锁店收银系统
唯鹿1 天前
Flutter如何适配RTL
android·flutter
Mis_wenwen1 天前
flutter 解决webview加载重定向h5页面 返回重复加载问题
flutter·h5·webview·重定向
兰琛1 天前
Flutter 1.1:下载Flutter环境
android·flutter
吴胜ws1 天前
Flutter中的Future和Stream
flutter
sunly_2 天前
Flutter:常见的页面布局:上边内容可滚动,底部固定一个按钮
android·javascript·flutter