Flutter系列(十一)实现商城首页和商品详情页

基础工程:

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

一、前言

        本文用flutter实现商城首页商品详情页,效果如下图:

Flutter系列(十一)实现商城首页和商品详情页_第1张图片             Flutter系列(十一)实现商城首页和商品详情页_第2张图片

二、使用的组件

MasonryGridView.count   瀑布流插件,组合Expanded可以添加固定在底部的按钮组

三、完整代码

 3.1 商城首页

1)瀑布流官方插件文档:flutter_staggered_grid_view | Flutter Package (pub.dev)

pubspec.yaml   添加依赖:   

dependencies:
  flutter:
    sdk: flutter
  # 瀑布流插件
  flutter_staggered_grid_view: ^0.6.2
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';  

2)页面跳转动画特效:动画特效工具类   animationUtile.dart    

参考文章:Flutter系列(九)ListView实现新闻列表和正文布局_摸金青年v的博客-CSDN博客

3)顶部搜索框

参考文章:Flutter系列(八)搜索框详解_flutter 搜索框_摸金青年v的博客-CSDN博客

商城首页  shop.dart

import 'package:flutter/material.dart';
import 'package:flutter_play/search.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_play/animationUtile.dart';
import 'package:flutter_play/skuDetail.dart';


/*商城页*/
class ShopPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color(0xFFFBFBFB), //顶部背景色
        title: SearchAppBar(),  // 顶部搜素框(自定义组件)
      ),
      body: Column(
        children: [
          Container(
            margin: const EdgeInsets.all(8),
            width: 500,
            height: 180,
            child: Image.network('https://img-blog.csdnimg.cn/63efe7acbac74e7ebce85e3801f948e3.jpeg'
                , fit: BoxFit.fill, width: 500, height: 200), //可放轮播图 商品类别 筛选之类的
          ),
          Expanded(
            child: MasonryGridView.count( //瀑布流插件
              itemCount: listData.length, //条目个数:获取数据的个数
              scrollDirection: Axis.vertical, //默认主轴是垂直方向
              crossAxisCount: 2, //交叉轴(水平方向)列数
              mainAxisSpacing: 4, //主轴:垂直方向间距
              crossAxisSpacing: 4, //交叉轴:水平方向间距
              itemBuilder: _showSku,
              // shrinkWrap:true, //含义是真空压缩组件,对性能损耗很大,不建议使用
            ), //Expanded 解决GridView嵌套在Column中不兼容的问题
          )
        ]
      ),
    );
  }

  //sku列表展示
  Widget _showSku(context, index) {
    return Container(
      padding: const EdgeInsets.fromLTRB(8, 5, 8, 5),
      decoration: BoxDecoration(
          border: Border.all(
            color: const Color.fromRGBO(233, 233, 233, 0.9), //边框颜色
            width: 2, //边框宽度
        ), //边框
      ),
      child: GestureDetector(
        onTap: () {
          Navigator.of(context).push(showPageFromRight(SkuDetailPage()));  //点击跳转到商品详情页
        },
        child: Column(
          children: [
            Image.network(listData[index]["imageUrl"],
                height: 150,
                width: double.infinity, //宽度撑满
                fit: BoxFit.cover //图片撑满父容器,截断
            ),
            Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround, //沿着主轴方向(垂直方向)间距等分
                crossAxisAlignment: CrossAxisAlignment.start, //水平方向左侧对齐
                children: [
                  Text(listData[index]["title"], style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
                      maxLines: 2, //最大显示两行
                      overflow: TextOverflow.ellipsis  //超出部分省略号
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween, //水平方向两端对齐
                    children: [
                      Row(
                        children: [
                          const Text(' ¥', style: TextStyle(color: Colors.redAccent, fontSize: 12, fontWeight: FontWeight.w600)),
                          Text(listData[index]["price"], style: const TextStyle(color: Colors.redAccent, fontSize: 12, fontWeight: FontWeight.w600)),
                        ],
                      ),
                      Row(
                        children: [
                          const Text('销量 ', style: TextStyle(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w600)),
                          Text(listData[index]["saleNum"], style: const TextStyle(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w600)), //数据类型要准确,Text必须是String
                        ],
                      ),
                    ],
                  ),
                ]
            )
          ],
        ),
      )
    );
  }

  //数据
  List listData = [
    {
      "title": "OPPO K9x 8GB+128GB 银紫超梦 天玑810 5000mAh长续航 33W快充 90Hz电竞",
      "price": "2788",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "3000"
    },
    {
      "title": "Redmi K60 骁龙8+处理器 2K高光屏 6400万超清相机 5500mAh长续航",
      "price": "2998",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "500"
    },
    {
      "title": "Apple iPhone 14 Pro (A2892) 256GB 暗紫色 支持移动联通电信5G 双卡双待手机",
      "price": "5899",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "2400"
    },
    {
      "title": "三星 SAMSUNG Galaxy Z Fold4 沉浸大屏体验 PC般强大生产力 12GB+512GB",
      "price": "4677",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "600"
    },
    {
      "title": "华为畅享 60X 7000mAh长续航 6.95英寸 影音大屏 256GB 曜金黑 鸿蒙智能手机",
      "price": "3778",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "900"
    },
    {
      "title": "荣耀X30 骁龙6nm疾速5G芯 66W超级快充 120Hz全视屏 全网通版",
      "price": "4999",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "500"
    },
    {
      "title": "Apple iPhone 14 Pro (A2892) 256GB 暗紫色 支持移动联通电信5G 双卡双待手机",
      "price": "5899",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "300"
    },
    {
      "title": "三星 SAMSUNG Galaxy Z Fold4 沉浸大屏体验 PC般强大生产力 12GB+512GB",
      "price": "4677",
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
      "saleNum": "100"
    }
  ];

}

3.2  商品详情页

import 'package:flutter/material.dart';
import 'package:flutter_play/animationUtile.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:flutter_play/cart.dart';

/*商品详情页*/
class SkuDetailPage extends StatefulWidget {
  @override
  State createState() => _SkuDetailPage();
}

class _SkuDetailPage extends State {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        foregroundColor: Colors.black, //字体颜色
        backgroundColor: const Color(0xFFFBFBFB), //顶部背景色
        title: const Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Text('商品', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
            Text('评价', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
            Text('详情', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
            Text('推荐', style: TextStyle(fontWeight: FontWeight.w500, fontSize: 15)),
          ],
        ),
        actions: [
          IconButton(
            onPressed: () {},
            icon: const Icon(Icons.share),
          ),
          IconButton(
            onPressed: () {},
            icon: const Icon(Icons.more_horiz),
          ),
        ],
      ),
      body: Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              child: Column(
                children: [
                  Image.network('https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg', fit: BoxFit.fill, width: 500, height: 260), // 图片
                  priceInfo(), // 价格和促销
                  skuInfo(), // 商品参数
                  comment(), // 评价
                ],
              ),
            ),
          ),
          bottomFix() //底部固定栏
        ],
      )
    );
  }

  /*价格信息*/
  Container priceInfo(){
    return Container(
      width: 500,
      height: 45,
      decoration: BoxDecoration(
        color: const Color.fromARGB(50, 255, 48, 48),
        borderRadius: BorderRadius.circular(6), // 设置圆角
      ),
      margin: const EdgeInsets.all(5),
      padding: const EdgeInsets.all(5),
      child: const Column(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('到手价  ¥4999', style: TextStyle(color: Colors.redAccent, fontSize: 14, fontWeight: FontWeight.w600)),
          Text('立减20 | 30天价保 | 包含运费险', style: TextStyle(color: Colors.redAccent, fontSize: 12, fontWeight: FontWeight.w400)),
        ],
      ),
    );
  }

  /*商品名称+选品+地址+时效*/
  Container skuInfo(){
    return Container(
      width: 500,
      height: 200,
      margin: const EdgeInsets.all(5),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
      ),
      child: const Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          Text('Apple iPhone 14 Pro (A2892) 256GB 暗紫色 支持移动联通电信5G 双卡双待手机', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600)), //商品名称
          Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween, //水平方向两端对齐
              children: [
                Text('选品 ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
                Text('已选: 暗紫色 1件 256G 0.33kg', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
                Icon(Icons.arrow_forward_ios, size: 10)
              ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween, //水平方向两端对齐
            children: [
              Text('参数 ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
              Column(
                children: [
                  Text('A15  ', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
                  Text('cpu  ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
                ],
              ),
              Column(
                children: [
                  Text('6.1英寸  ', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
                  Text('尺寸  ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
                ],
              ),
              Column(
                children: [
                  Text('OLED直屏  ', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
                  Text('屏幕材质  ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
                ],
              ),
              Icon(Icons.arrow_forward_ios, size: 10)
            ],
          ),
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween, //水平方向两端对齐
            children: [
              Text('配送 ', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
              Column(
                children: [
                  Text('北京 海淀区', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
                  Text('现货,预计今天(06月23日)送达', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
                ],
              ),
              Icon(Icons.arrow_forward_ios, size: 10)
            ],
          )
        ],
      ),
    );
  }

  /*评价*/
  Container comment(){
    return Container(
      width: 500,
      height: 190,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10), // 设置圆角
      ),
      margin: const EdgeInsets.all(10),
      padding: const EdgeInsets.only(top: 5),
      // alignment: Alignment.centerLeft,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          const Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween, //水平方向两端对齐
            children: [
              Text('评价 (200万+)', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
              Row(
                children: [
                  Text('好评度 97%', style: TextStyle(color: Colors.grey, fontSize: 12, fontWeight: FontWeight.w400)),
                  Icon(Icons.arrow_forward_ios, size: 10)
                ],
              ),
            ],
          ),
          SizedBox(height: 2),
          Row(
            children: [
              ClipOval(
                  child: Image.network('https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg', width: 30, height: 30, fit: BoxFit.cover),
              ), //圆型头像
              const Text(' 吴邪', style: TextStyle(color: Colors.grey, fontSize: 14, fontWeight: FontWeight.w400)),
            ],
          ),
          SizedBox(height: 2),
          const Text('外观: 小巧精致,手感不错,屏幕流畅,拍照功能强大', style: TextStyle(fontSize: 14, fontWeight: FontWeight.w400)),
          SizedBox(height: 3),
          Expanded(
            child: MasonryGridView.count(
              scrollDirection: Axis.horizontal, //水平方向
              itemCount: listData.length, //条目个数:获取数据的个数
              crossAxisCount: 1, //交叉轴(水平方向)行数
              mainAxisSpacing: 4,
              crossAxisSpacing: 4,
              itemBuilder: (context, index) {
                return Image.network(listData[index]["imageUrl"], width: 120, height: 50, fit: BoxFit.cover);
              }
            ),
          )//图片组
        ],
      ),
    );
  }

  List listData = [
    {
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    },
    {
      "imageUrl": "https://img-blog.csdnimg.cn/c6dfd375abf1433fa3a42951cc186a2b.jpeg",
    }
  ];

  /*底部固定:购物车+立即购买*/
  Container bottomFix(){
    return Container(
      width: 500,
      height: 50,
      color: Colors.white,
      // alignment: Alignment.centerLeft,
      child: ButtonBar(
          children: [
            IconButton(
              color: Colors.grey,
              icon: const Icon(Icons.add_business), //店铺
              onPressed:(){},
            ),
            IconButton(
              color: Colors.grey,
              icon: const Icon(Icons.star_border_outlined), //收藏
              onPressed:(){},
            ),
            IconButton(
              color: Colors.grey,
              icon: const Icon(Icons.add_shopping_cart), //购物车
              onPressed:(){
                Navigator.of(context).push(showPageFromRight(CartPage())); //点击购物车图标,进入购物车页
              },
            ),
            TextButton  (
              style: ButtonStyle(
                  minimumSize: MaterialStateProperty.all(const Size(80, 50)),
                  foregroundColor: MaterialStateProperty.all(Colors.pinkAccent), //字体颜色
                  shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))), //圆角
                  side: MaterialStateProperty.all(const BorderSide(color: Colors.pinkAccent, width: 0.67)) // 边框颜色
              ),
              child: const Text('加入购物车'),
              onPressed: () {

              },
            ),
            TextButton (
              style: ButtonStyle(
                  minimumSize: MaterialStateProperty.all(const Size(80, 50)),
                  backgroundColor: MaterialStateProperty.all(Colors.pinkAccent),
                  foregroundColor: MaterialStateProperty.all(Colors.white), //字体颜色
                  shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(10))) //圆角
              ),
              child: const Text('立即购买'),
              onPressed: () {

              },
            ),
          ]
      ),
    );
  }

}

四、解决问题

4.1 商品详情页-评论模块的,图片组支持水平滑动,使用的还是瀑布流组件,需要设置参数:

1.设置了水平方向为主轴,表示滚动方向为水平

2.设置了交叉轴行数等于1,表示图片只有一行显示

MasonryGridView.count(
              scrollDirection: Axis.horizontal, //水平方向
              itemCount: listData.length, //条目个数:获取数据的个数
              crossAxisCount: 1, //交叉轴(水平方向)行数
              mainAxisSpacing: 4,
              crossAxisSpacing: 4,
              itemBuilder: (context, index) {
                return Image.network(listData[index]["imageUrl"], width: 120, height: 50, fit: BoxFit.cover);
              }
            ),

本文结束

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