Flutter系列(十二)实现购物车和提交订单页

基础工程:

Flutter系列(十一)实现商城首页和商品详情页_摸金青年v的博客-CSDN博客

Flutter系列(四)底部导航+顶部导航+图文列表完整代码_摸金青年v的博客-CSDN博客

一、前言

        本文用flutter实现购物车提交订单页,效果如下图:

Flutter系列(十二)实现购物车和提交订单页_第1张图片               Flutter系列(十二)实现购物车和提交订单页_第2张图片

二、使用组件

1. Card 卡片组件,扁平化风格

2. CheckBox 复选框组件,实现全选的功能

3. 数量加减插件    NumberControllerWidget

参考:flutter 自定义TextField,加减数量输入框 - 简书 (jianshu.com)

调整了下icon(加号和减号)的大小

三、完整代码

3.1 购物车页  cart.dart

import 'package:flutter/material.dart';
import 'package:flutter_play/NumberControllerWidget.dart';
import 'package:flutter_play/animationUtile.dart';
import 'package:flutter_play/checkout.dart';

/*购物车页*/
class CartPage extends StatefulWidget {
  @override
  State createState() => _CartPage();
}

class _CartPage extends State {

  List isChecks = [false, false, false];  //复选框状态,默认未选中
  bool isAllSelect = false;  //全选状态
  List listData = [
    {
      "store": "Apple苹果旗舰店",
      "skuName": "Apple iPhone 14 Pro (A2892) 256GB 暗紫色 支持移动联通电信5G 双卡双待手机",
      "price": "5988",
      "image": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "store": "小米旗舰店",
      "skuName": "Redmi K60 骁龙8+处理器 2K高光屏 6400万超清相机 5500mAh长续航",
      "price": "2588",
      "image": "https://img-blog.csdnimg.cn/678c0686dc694b65ad6b20693dbc35f1.jpeg",
    },
    {
      "store": "耐克品牌店",
      "skuName": "夏季新款潮流鞋",
      "price": "1299",
      "image": "https://img-blog.csdnimg.cn/63efe7acbac74e7ebce85e3801f948e3.jpeg",
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('购物车', style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
        foregroundColor: Colors.black, //字体颜色
        backgroundColor: const Color(0xFFFBFBFB), //顶部背景色
      ),
      body: Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  skuList(),//商品列表
                ],
              ),
            )
          ),
          bottomFix() //底部固定栏
        ],
      ),
    );
  }

  //商品列表
  Container skuList(){
    return Container(
      width: 500,
      height: 800,
      padding: const EdgeInsets.only(top: 5),
      child: ListView.builder(
        itemCount: listData.length,  //商品个数
        itemBuilder: (context, index) {
          return Container(
            height: 200,
            width: 500,
            padding: const EdgeInsets.fromLTRB(5, 5, 5, 5), //内边距
            margin: const EdgeInsets.fromLTRB(8, 5, 8, 0), //外边距
            child: Card(
              clipBehavior: Clip.hardEdge,
              elevation: 2, //卡片海拔高度设置,立体感
              child: InkWell(
                splashColor: Colors.blue.withAlpha(30), //点击卡片,有蓝色透明度响应,扁平化
                onTap: () {

                }, //卡片点击
                child: Column(
                  children: [
                    Row(
                      children: [
                        Checkbox(
                          value: isChecks[index],
                          onChanged:(value){
                            setState(() {
                              isChecks[index] = value!;
                            });
                          } ,
                        ),//复选框
                        Text(listData[index]["store"], style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), //店铺
                      ],
                    ),
                    Row(
                      children: [
                        Checkbox(
                          value: isChecks[index],
                          onChanged:(value){
                            setState(() {
                              isChecks[index] = value!;
                            });
                          } ,
                        ),
                        Image.network(listData[index]["image"], width: 90, height: 90, fit: BoxFit.cover),//商品图片
                        Container(
                            width: 200,
                            height: 100,
                            padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              children: [
                                Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,  //水平左对齐
                                  children: [
                                    Text(listData[index]["skuName"], style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis),
                                    Container(
                                        height: 20,
                                        width: 150,
                                        padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
                                        decoration: BoxDecoration(
                                          color: const Color(0xFFF2F2F2),
                                          borderRadius: BorderRadius.circular(4), // 设置圆角
                                        ),
                                        child: const Row(
                                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                          children: [
                                            Text('蓝色,64G,WLAN版', style: TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w900)),
                                            Icon(Icons.arrow_forward_ios, size: 12, color: Colors.grey)
                                          ],
                                        )
                                    ), //选品
                                  ],
                                ),
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                  children: [
                                    Row(
                                      children: [
                                        const Text('¥', style: TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//金额
                                        Text(listData[index]["price"], style: const TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//金额
                                      ],
                                    ),
                                    NumberControllerWidget(
                                      addValueChanged: (num){print(num);},
                                      removeValueChanged: (num){print(num);},
                                      updateValueChanged: (num){},
                                    )//选件组件
                                  ],
                                )
                              ],
                            )
                        )
                      ],
                    ),
                  ],
                ),
              ),
            ),
          );
        },
      ),
    );
  }

  /*底部固定:去结算*/
  Container bottomFix(){
    return Container(
      width: 500,
      height: 50,
      color: Colors.white,
      padding: const EdgeInsets.all(8),
      child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Row(
              children: [
                Checkbox(
                  value: isAllSelect,
                  onChanged:(value){
                    setState(() {
                      isAllSelect = value!;
                      //全选页面所有复选框
                      for (var i = 0; i < isChecks.length; i++) {
                        isChecks[i] = isAllSelect;
                      }
                    });
                  } ,
                ),//复选框
                const Text(' 全选', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//
              ],
            ),
            const Row(
              children: [
                Text('合计金额: ', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
                Text('¥', style: TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//金额
                Text('7988', style: TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//金额
              ],
            ),
            TextButton (
              style: ButtonStyle(
                  minimumSize: MaterialStateProperty.all(const Size(80, 30)),
                  backgroundColor: MaterialStateProperty.all(Colors.blueAccent),
                  foregroundColor: MaterialStateProperty.all(Colors.white), //字体颜色
                  shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))) //圆角
              ),
              child: const Text('去结算'),
              onPressed: () {
                Navigator.of(context).push(showPageFromRight(CheckOutPage()));
              },
            ),
          ]
      ),
    );
  }

}

3.2 提交订单页  checkout.dart

import 'package:flutter/material.dart';
import 'package:flutter_play/NumberControllerWidget.dart';

/*结算页*/
class CheckOutPage extends StatefulWidget {
  @override
  State createState() => _CheckOutPage();
}

class _CheckOutPage extends State {

  List listData = [
    {
      "store": "Apple苹果旗舰店",
      "skuName": "Apple iPhone 14 Pro (A2892) 256GB 暗紫色 支持移动联通电信5G 双卡双待手机",
      "price": "¥ 5988",
      "image": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "store": "小米旗舰店",
      "skuName": "Redmi K60 骁龙8+处理器 2K高光屏 6400万超清相机 5500mAh长续航",
      "price": "¥ 2588",
      "image": "https://img-blog.csdnimg.cn/678c0686dc694b65ad6b20693dbc35f1.jpeg",
    },
  ];

  var userInfo = {
    "nickName": "吴邪",
    "phone": "139343254540",
    "address": "北京市 海淀区 天秀路",
  };

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        foregroundColor: Colors.black, //字体颜色
        backgroundColor: const Color(0xFFFBFBFB), //顶部背景色
        title: const Text('提交订单', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 16)),
      ),
      body: Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  addressInfo(), //地址选择
                  skuInfo(), //商品
                  priceInfo(),   //金额优惠
                ],
              ),
            ),
          ),
          bottomFix() //固定页面底部
        ],
      )
    );
  }

  Container addressInfo(){
    return Container(
      height: 90,
      width: 500,
      margin: const EdgeInsets.fromLTRB(8, 5, 8, 5),
      child: Card(
        clipBehavior: Clip.hardEdge,
        elevation: 2,
        child: InkWell(
          splashColor: Colors.blue.withAlpha(30),
          onTap: () {
               //弹出地址管理弹窗
          }, //卡片点击
          child: Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Column(
                  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                  crossAxisAlignment: CrossAxisAlignment.start,  //水平左对齐
                  children: [
                    Text(userInfo["address"]!, style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 14)),
                    Row(
                      children: [
                        Text(userInfo["nickName"]!, style: const TextStyle(fontWeight: FontWeight.w300, fontSize: 14)),
                        Padding(
                          padding: const EdgeInsets.fromLTRB(20, 0, 0, 0),
                          child: Text(userInfo["phone"]!, style: const TextStyle(fontWeight: FontWeight.w300, fontSize: 14)),
                        ),
                      ],
                    )
                  ],
                ),
                const Icon(Icons.arrow_forward_ios, size: 12)
              ],
            ),
          )
        ),
      )
    );
  }

  //商品列表
  SizedBox skuInfo(){
    return SizedBox(
      width: 500,
      height: 370,
      child: ListView.builder(
        itemCount: listData.length,
        itemBuilder: (context, index) {
          return Container(
            height: 180,
            width: 500,
            margin: const EdgeInsets.fromLTRB(10, 5, 10, 5),
            child: Card(
              clipBehavior: Clip.hardEdge,
              elevation: 2,
              child: Padding(
                padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.spaceAround,
                  children: [
                    Row(
                      children: [
                        Text(listData[index]["store"], style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
                      ],
                    ),
                    Row(
                      children: [
                        Image.network(listData[index]["image"], width: 90, height: 90, fit: BoxFit.cover),//商品图片
                        Container(
                            width: 240,
                            height: 90,
                            padding: const EdgeInsets.fromLTRB(8, 0, 8, 0),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.spaceBetween,
                              crossAxisAlignment: CrossAxisAlignment.start,  //水平左对齐
                              children: [
                                Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,  //水平左对齐
                                  children: [
                                    Text(listData[index]["skuName"], style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600), maxLines: 1, overflow: TextOverflow.ellipsis),
                                    Container(
                                        height: 20,
                                        width: 150,
                                        padding: const EdgeInsets.fromLTRB(5, 0, 5, 0),
                                        decoration: BoxDecoration(
                                          color: const Color(0xFFF2F2F2),
                                          borderRadius: BorderRadius.circular(4), // 设置圆角
                                        ),
                                        child: const Row(
                                          mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                          children: [
                                            Text('蓝色,64G,WLAN版', style: TextStyle(fontSize: 12, color: Colors.grey, fontWeight: FontWeight.w900)),
                                          ],
                                        )
                                    ),
                                  ],
                                ),
                                Row(
                                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                                  children: [
                                    Text(listData[index]["price"], style: const TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//金额
                                    NumberControllerWidget(
                                      addValueChanged: (num){print(num);},
                                      removeValueChanged: (num){print(num);},
                                      updateValueChanged: (num){},
                                    )//选件组件
                                  ],
                                )
                              ],
                            )
                        )
                      ],
                    ),
                  ],
                ),
              )

            ),
          );
        },
      ),
    );
  }

  Container priceInfo(){
    return Container(
      height: 180,
      width: 500,
      margin: const EdgeInsets.fromLTRB(8, 5, 8, 5),
      child: const Card(
        clipBehavior: Clip.hardEdge,
        elevation: 2,
        child: Padding(
          padding: EdgeInsets.fromLTRB(10, 5, 10, 5),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text('商品金额', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                  Text('¥5988', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text('运费', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                  Text('¥8', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text('优惠券', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                  Text('-¥20', style: TextStyle(color: Colors.redAccent, fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text('积分', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                  Text('-¥3', style: TextStyle(color: Colors.redAccent, fontSize: 14, fontWeight: FontWeight.w400)),//商品名称
                ],
              )
            ],
          ),
        )
      )
    );
  }

  /*底部固定:提交订单*/
  Container bottomFix(){
    return Container(
      width: 500,
      height: 50,
      color: Colors.white,
      child: Padding(
        padding: const EdgeInsets.fromLTRB(10, 0, 10, 0),
        child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              const Row(
                children: [
                  Text('应支付: ', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),//
                  Text('¥5968', style: TextStyle(color: Colors.redAccent, fontSize: 16, fontWeight: FontWeight.w600)),//商品名称
                ],
              ),
              TextButton (
                style: ButtonStyle(
                    minimumSize: MaterialStateProperty.all(const Size(90, 30)),
                    backgroundColor: MaterialStateProperty.all(Colors.blueAccent),
                    foregroundColor: MaterialStateProperty.all(Colors.white), //字体颜色
                    shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))) //圆角
                ),
                child: const Text('提交订单'),
                onPressed: () {
                  //跳转收银台
                },
              ),
            ]
        ),
      )
    );
  }

}

 

 四、解决问题

4.1. 全选逻辑实现

1)如果点击非全选框,两种状态:未选择和选中,其他复选框之间是否选中不能互相影响,所以会有取值数组,保存每个复选框的状态

List isChecks = [false, false, false];  //其他复选框的状态,默认未选中

Checkbox(
     value: isChecks[index],
          onChanged:(value){
          setState(() {
                isChecks[index] = value!;  //点击则逻辑取反,false和true直接来回转换
              });
       } ,
 ),//复选框

2)如果点击全选框,需要更新页面其他复选框的状态和全选框保持一致,需要循环处理,

bool isAllSelect = false;  //全选框的状态

Checkbox(
        value: isAllSelect,
        onChanged:(value){
            setState(() {
                isAllSelect = value!;  //全选框逻辑取反
                for (var i = 0; i < isChecks.length; i++) {
                    isChecks[i] = isAllSelect;  //页面所有复选框, 状态和全选框保持一致
                }
              });
      } ,
),//复选框

本文结束

 

你可能感兴趣的:(flutter,flutter)