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();
      },
    );
  }
}
相关推荐
江上清风山间明月14 小时前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能1 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人1 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen1 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang1 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang1 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1231 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11192 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力2 天前
Flutter应用开发:对象存储管理图片
flutter