Flutter:商品多规格内容总结,响应式数据,高亮切换显示。

如图所示:
在这里插入图片描述

代码为练习时写的项目,写的一般,功能实现了,等以后再来优化。

自己模拟的数据结构

var data = {'id':1,'name':'精品小米等多种五谷杂粮精品小等多种五谷杂粮','logo':'https://cdn.uviewui.com/uview/swiper/1.jpg','price':100.5,'old_price':200.0,'stock':998,'images':[{'id':1,'src':'https://cdn.uviewui.com/uview/swiper/1.jpg',},{'id':2,'src':'https://cdn.uviewui.com/uview/swiper/2.jpg',},{'id':3,'src':'https://cdn.uviewui.com/uview/swiper/3.jpg',}],'sku':[{'name':'颜色','list':[{'id':8,'name':'红色',},{'id':9,'name':'蓝色'},{'id':10,'name':'白加黑'},{'id':11,'name':'紫色'},{'id':12,'name':'绿色'},]},{'name':'尺码','list':[{'id':13,'name':'S'},{'id':14,'name':'M'},{'id':15,'name':'L'},{'id':16,'name':'XL'},{'id':17,'name':'XXL'},]},]
};

GoodsModel

import 'package:get/get.dart';class GoodsModel {final int id;final String name;final String logo;final double price;final double oldPrice;final int stock;final List<Image> images;final List<Sku> sku;GoodsModel({required this.id,required this.name,required this.logo,required this.price,required this.oldPrice,required this.stock,required this.images,required this.sku,});// 从JSON对象创建GoodsModel实例factory GoodsModel.fromJson(Map<String, dynamic> json) {return GoodsModel(id: json['id'] as int,name: json['name'] as String,logo: json['logo'] as String,price: json['price'] as double,oldPrice: json['old_price'] as double,stock: json['stock'] as int,images: List.from(json['images'] as List).map((e) => Image.fromJson(e)).toList(),sku: List.from(json['sku'] as List).map((e) => Sku.fromJson(e)).toList(),);}// 将GoodsModel实例转换为JSON对象Map<String, dynamic> toJson() {return {'id': id,'name': name,'logo': logo,'price': price,'old_price': oldPrice,'stock': stock,'images': images.map((e) => e.toJson()).toList(),'sku': sku.map((e) => e.toJson()).toList(),};}
}class Image {final int id;final String src;Image({required this.id,required this.src,});// 从JSON对象创建Image实例factory Image.fromJson(Map<String, dynamic> json) {return Image(id: json['id'] as int,src: json['src'] as String,);}// 将Image实例转换为JSON对象Map<String, dynamic> toJson() {return {'id': id,'src': src,};}
}class Sku {final String name;final List<SkuItem> list;Sku({required this.name,required this.list,});// 从JSON对象创建Sku实例factory Sku.fromJson(Map<String, dynamic> json) {return Sku(name: json['name'] as String,list: List.from(json['list'] as List).map((e) => SkuItem.fromJson(e)).toList(),);}// 将Sku实例转换为JSON对象Map<String, dynamic> toJson() {return {'name': name,'list': list.map((e) => e.toJson()).toList(),};}
}class SkuItem {final int id;final String name;RxBool show; // 自定义响应式变量,判断当前项是否选中SkuItem({required this.id,required this.name,bool showValue = false, // 默认false}) : show = RxBool(showValue);// 从JSON对象创建SkuItem实例factory SkuItem.fromJson(Map<String, dynamic> json) {return SkuItem(id: json['id'] as int,name: json['name'] as String,showValue: false, // JSON 数据中没有 show 字段,所以自定义设置为 false);}// 将SkuItem实例转换为JSON对象Map<String, dynamic> toJson() {return {'id': id,'name': name,};}
}

GoodsModel中,要确保sku下的SkuItem中,每一项都需要添加一个动态响应式数据show字段来实现规格切换高亮显示,

controller

import 'package:flutter_aidishi/utils/loading.dart';
import 'package:get/get.dart';
import '../../../models/home/goods_detail.dart';class GoodsDetailController extends GetxController {GoodsDetailController();final ProductId = Get.arguments['id'];GoodsModel? goodsDetail; // 商品详情List<Image> bannerList = []; // 商品列表RxList<Sku> skuList = RxList<Sku>([]); // 直接指定类型为RxListList skuSelected = []; // 选中后的skubool loading = false; // 提交状态int type = 1; // 下单状态,1加入购物车,2立即购买RxInt payNum = 1.obs; // 下单数量_initData() {// 模拟接口请求var data = {'id':1,'name':'精品小米等多种五谷杂粮精品小等多种五谷杂粮','logo':'https://cdn.uviewui.com/uview/swiper/1.jpg','price':100.5,'old_price':200.0,'stock':998,'images':[{'id':1,'src':'https://cdn.uviewui.com/uview/swiper/1.jpg',},{'id':2,'src':'https://cdn.uviewui.com/uview/swiper/2.jpg',},{'id':3,'src':'https://cdn.uviewui.com/uview/swiper/3.jpg',}],'sku':[{'name':'颜色','list':[{'id':8,'name':'红色',},{'id':9,'name':'蓝色'},{'id':10,'name':'白加黑'},{'id':11,'name':'紫色'},{'id':12,'name':'绿色'},]},{'name':'尺码','list':[{'id':13,'name':'S'},{'id':14,'name':'M'},{'id':15,'name':'L'},{'id':16,'name':'XL'},{'id':17,'name':'XXL'},]},]};goodsDetail = GoodsModel.fromJson(data);bannerList = goodsDetail!.images;// 使用assignAll方法将普通的List<Sku>赋值给RxList<Sku>。这是GetX库中推荐的方法来填充响应式列表。skuList.assignAll(goodsDetail!.sku); // 正确地将List<Sku>转换为RxList<Sku>update(["goods_detail"]);}// 更改数量onTapChangePayNum(int value){payNum.value = value;}// SKU切换void onTapChangeSku(index,i){// 先循环,当前点击的小规格默认全部为falseskuList[index].list.forEach((sku)=> sku.show.value = false);// 在赋值,当前点击项trueskuList[index].list[i].show.value = true;update(["goods_detail"]);}// 下单void submit(){// 每次先清空,在去把已选择的sku放到skuSelected中skuSelected.clear();skuList.forEach((item){item.list.forEach((val){if(val.show.value){skuSelected.add(val);}});});if(skuSelected.length != skuList.length){Loading.error('请先选择完整规格');}else{print('下单方式:$type,购买数量:$payNum,看看选择了哪些规格:${skuSelected[0].id},${skuSelected[1].id}');}}@overridevoid onInit() {super.onInit();}@overridevoid onReady() {super.onReady();_initData();}@overridevoid onClose() {super.onClose();}
}

view

import 'package:flutter/material.dart';
import 'package:flutter_aidishi/extension/index.dart';
import 'package:flutter_aidishi/widget/index.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:flutter_swiper_null_safety/flutter_swiper_null_safety.dart';import 'index.dart';class GoodsDetailPage extends GetView<GoodsDetailController> {const GoodsDetailPage({super.key});//  轮播图Widget _buildBanner() {return Container(height: 312.w,child: Swiper(autoplay: true, // 自动轮播itemCount: 2, // 轮播数量loop: true, // 循环pagination: const SwiperPagination(alignment: Alignment.bottomCenter, // 指示器位置builder: TDSwiperPagination.dotsBar, // 具体样式),itemBuilder: (BuildContext context, int index) {return GestureDetector(onTap: (){print('点击了第$index个图片');},// child: TDImage(imgUrl: controller.bannerList[index].src,),child: TDImage(assetUrl: 'assets/images/banner.png',type: TDImageType.square,),);},),);}// 商品信息Widget _buildGoodsName() {return Container(padding: EdgeInsets.only(left: 15.w,right: 15.w,top: 20.w,bottom: 20.w),width: 375.w,color: Colors.white,child: <Widget>[Text("${controller.goodsDetail?.name}",maxLines: 2,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 15.sp,fontWeight: FontWeight.bold),),SizedBox(height: 15.w,),<Widget>[<Widget>[Text('¥',style: TextStyle(fontSize: 13.sp,color: AppColors.mainColor),),Text('${controller.goodsDetail?.price}',style: TextStyle(fontSize: 24.sp,color: AppColors.mainColor),),Text('/现价',style: TextStyle(fontSize: 13.sp,color: AppColors.mainColor),),SizedBox(width: 10.w,),Text('¥${controller.goodsDetail?.oldPrice}/原价',style: TextStyle(fontSize: 14.sp,color: AppColors.Color999,decoration: TextDecoration.lineThrough,decorationColor: AppColors.Color999),)].toRow(crossAxisAlignment: CrossAxisAlignment.baseline,textBaseline: TextBaseline.alphabetic),Text('库存 ${controller.goodsDetail?.stock}',style: TextStyle(fontSize: 11.sp,color: AppColors.Color999),)].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween)].toColumn(crossAxisAlignment: CrossAxisAlignment.start),);}// 商品详情Widget _buildGoodsDetail() {return <Widget>[SizedBox(height: 10.w,),<Widget>[Container(width: 50.w,height: 1,color: Color(0xffe1e1e1),),SizedBox(width: 10.w,),Text('商品详情',style: TextStyle(fontSize: 11.sp,color: AppColors.Color999),),SizedBox(width: 10.w,),Container(width: 50.w,height: 1,color: Color(0xffe1e1e1),),].toRow(mainAxisAlignment: MainAxisAlignment.center,),SizedBox(height: 10.w,),Container(height: 500.w,color: Colors.blueGrey,child: Center(child: Text('富文本'),),)].toColumn();}// 可滚动内容区域Widget _buildTop(){return SingleChildScrollView(child: <Widget>[_buildBanner(),SizedBox(height: 15.w,),_buildGoodsName(),_buildGoodsDetail(),].toColumn(),);}// 底部悬浮按钮Widget _buildGoodsFoot(BuildContext context) {return <Widget>[GestureDetector(onTap: (){print('弹出购物车');controller.type = 1;Navigator.of(context).push(TDSlidePopupRoute(modalBarrierColor: TDTheme.of(context).fontGyColor2,slideTransitionFrom: SlideTransitionFrom.bottom,builder: (context) {return Container(width: 375.w,height: 500.w,decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.only(topLeft: Radius.circular(10.w),topRight: Radius.circular(10.w)),),child: _buildFootPopup(context),);}));},child: Container(width: 187.5.w,height: 49.w,color: Colors.white,child: Center(child: Text('加入购物车',style: TextStyle(fontSize: 14.sp),),),)),GestureDetector(onTap: (){print('立即结算');controller.type = 2;Navigator.of(context).push(TDSlidePopupRoute(modalBarrierColor: TDTheme.of(context).fontGyColor2,slideTransitionFrom: SlideTransitionFrom.bottom,builder: (context) {return Container(width: 375.w,height: 500.w,decoration: BoxDecoration(color: Colors.white,borderRadius: BorderRadius.circular(10.w)),child: _buildFootPopup(context),);}));},child: Container(width: 187.5.w,height: 49.w,color: AppColors.mainColor,child: Center(child: Text('立即购买',style: TextStyle(fontSize: 14.sp,color: AppColors.Colorfff),),),)),].toRow();}// 多规格弹窗Widget _buildFootPopup(BuildContext context){return Container(padding: EdgeInsets.all(15.w),child: <Widget>[// 商品信息<Widget>[TDImage(assetUrl: 'assets/images/goods.png',type: TDImageType.roundedSquare,width: 74,height: 74,),Container(width: 220.w,child: <Widget>[Text('精品小米等多种五谷杂粮精品小米等多精品小米等多种五谷杂粮精品小米等多种五谷杂粮种五谷杂粮',maxLines: 2,overflow: TextOverflow.ellipsis,style: TextStyle(fontSize: 14.sp,fontWeight: FontWeight.bold),),SizedBox(height: 10.w,),<Widget>[Text('¥',style: TextStyle(fontSize: 12.sp,color: AppColors.mainColor),),Text('2500',style: TextStyle(fontSize: 16.sp,color: AppColors.mainColor),),Text('/现价',style: TextStyle(fontSize: 12.sp,color: AppColors.mainColor),),SizedBox(width: 10.w,),Text('¥2000/原价',style: TextStyle(fontSize: 10.sp,color: AppColors.Color999,decoration: TextDecoration.lineThrough,decorationColor: AppColors.Color999),)].toRow(crossAxisAlignment: CrossAxisAlignment.baseline,textBaseline: TextBaseline.alphabetic)].toColumn(),),GestureDetector(onTap: ()=> Navigator.of(context).pop(),child: TDImage(assetUrl: 'assets/images/close.png',type: TDImageType.roundedSquare,width: 20,height: 20,),)].toRow(crossAxisAlignment: CrossAxisAlignment.start,mainAxisAlignment: MainAxisAlignment.spaceBetween),SizedBox(height: 15.w,),Container(width: 345.w,height: 1,color: Color(0xfff5f5f5),),SizedBox(height: 10.w,),// 库存<Widget>[Text('库存:886',style: TextStyle(fontSize: 13.sp),),<Widget>[Text('数量',style: TextStyle(fontSize: 13.sp),),SizedBox(width: 10.w,),// 选择数量TDStepper(theme: TDStepperTheme.filled,value:controller.payNum.value,min:1,onChange:(value){controller.onTapChangePayNum(value);})].toRow()].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween),SizedBox(height: 20.w,),Container(height: 260.w, // 固定高度color: Colors.white, // 可选:可改成灰色,查看滚动区域child: SingleChildScrollView(// 如果内容在垂直方向上超出Container的高度,则可以滚动child: Column(// 使用Column来垂直排列子组件children: <Widget>[// 大规格for(var index = 0;index<controller.skuList.length;index++)<Widget>[Container(width: 375.w,child: Text('${controller.skuList[index].name}'),),SizedBox(height: 10.w,),<Widget>[// 小规格// for(var val in item.list)for(var i = 0;i<controller.skuList[index].list.length;i++)GestureDetector(onTap: (){controller.onTapChangeSku(index,i);},child: Obx(() =>Container(margin: EdgeInsets.only(right: 20.w,bottom: 20.w,left: 0),padding: EdgeInsets.only(left: 30.w,right: 30.w,top: 6.w,bottom: 6.w),child: Text('${controller.skuList[index].list[i].name}'),decoration: BoxDecoration(color: controller.skuList[index].list[i].show.value ? Color(0xffffffff) : Color(0xffF8F8F8),borderRadius: BorderRadius.circular(5.w),border: Border.all(color: controller.skuList[index].list[i].show.value ? Color(0xffFF770F) : Color(0xff999999),width: 1.0)),)),)].toWrap()].toColumn(crossAxisAlignment: CrossAxisAlignment.start),// 你可以继续添加更多的子组件],),),),// 结算SizedBox(height: 20.w,),Container(width: 345.w,child: TDButton(text: '确定', // 按钮文案height: 43.w, // 自定义高度// 文案左侧图标,根据提交状态判断是否显示loadingiconWidget: controller.loading ? TDLoading(size: TDLoadingSize.small,icon: TDLoadingIcon.circle,) : Container(),size: TDButtonSize.large, // 按钮尺寸type: TDButtonType.fill, // 类型:填充,描边,文字theme: TDButtonTheme.primary, // 主题shape: TDButtonShape.round, // 形状:圆角,胶囊,方形,圆形,填充 rectangle, round, square, circle, filledisBlock: true, // 是否时BlockonTap:controller.submit, // 点击提交按钮触发padding: EdgeInsets.all(0),margin: EdgeInsets.all(0),),)].toColumn(),);}// 主视图Widget _buildView(BuildContext context) {return <Widget>[_buildTop().expanded(),_buildGoodsFoot(context)].toColumn();}@overrideWidget build(BuildContext context) {return GetBuilder<GoodsDetailController>(init: GoodsDetailController(),id: "goods_detail",builder: (_) {return Scaffold(appBar: AppBar(title: const Text("goods_detail")),body: _buildView(context),backgroundColor: Color(0xffF6F6F6),);},);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/485724.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

团队管理中如何做好目标管理

团队管理中的目标管理是确保团队高效运行的核心要素之一。 在目标管理中&#xff0c;清晰的目标设定、合理的资源分配、实时的跟踪与反馈机制是成功的关键。首先&#xff0c;设定SMART目标&#xff08;具体、可衡量、可达成、相关性强、时间限定&#xff09;能够有效聚焦团队的…

Unity在运行状态下,当物体Mesh网格发生变化时,如何让MeshCollider碰撞体也随之实时同步变化?

旧版源代码地址&#xff1a;https://download.csdn.net/download/qq_41603955/90087225?spm1001.2014.3001.5501 旧版效果展示&#xff1a; 新版加上MeshCollider后的效果&#xff1a; 注意&#xff1a;在Unity中&#xff0c;当你动态地更改物体的Mesh时&#xff0c;通常期望…

Blender导入下载好的fbx模型像的骨骼像针戳/像刺猬

为什么我下载下来的骨骼模型和我自己绑定的模型骨骼朝向完全不一样 左边是下载的模型 右边是我自己绑定的模型 左边的模型刚刚感觉都是像针一样往外戳的&#xff0c;像刺猬一样那种。 解决方法勾选自动骨骼坐标系

基于Springboot+Vue的在线答题闯关系统

基于SpringbootVue的在线答题闯关系统 前言&#xff1a;随着在线教育的快速发展&#xff0c;传统的教育模式逐渐向互联网教育模式转型。在线答题系统作为其中的一个重要组成部分&#xff0c;能够帮助用户通过互动式的学习方式提升知识掌握度。本文基于Spring Boot和Vue.js框架&…

矿区新发现,改造明星profinet转profibus协议网关也有未来

profinet转profibusDP协议网关&#xff0c;在矿区的一些老设备上&#xff0c;改造升级一定会遇到profibus无法通讯的情况&#xff0c;选择一个协议模块网关是性价比的首选&#xff0c;下面介绍一下协议网关的一些指标 PROFINET 在 PROFIBUS 一侧为 PROFIBUS DP 从站&#xff0…

高效查找秘密武器一:位图

有这样的一个问题&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数 中。 那么我们一般会想到这样做的 1.遍历&#xff0c;时间复杂度O(n) 2.排序&#xff08;N*logN&#xff09;&#xff0c…

《单片机原理及接口技术》(C51编程)(第三版)------张毅刚主编

1.整体框架&#xff1a;1-22题&#xff08;17-20为编程题分别源自数中的P98,P162,P177页&#xff09; 2.简答题部分&#xff1a; 3.计算题 4.程序题/编程题

Vision Transformer (ViT) 基本原理

Vision Transformer (ViT) 基本原理 flyfish Vision Transformer (ViT) 是一种基于 Transformer 架构的计算机视觉模型 一、ViT 的基本原理 ViT 的核心思想是将一张图像视为一组序列&#xff0c;将其嵌入到 Transformer 的输入中&#xff0c;通过自注意力机制捕获全局上下文…

工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet

论文&#xff1a;https://arxiv.org/pdf/2311.14897v3.pdf 项目&#xff1a;https://github.com/chopper-233/anomaly-shapenet 这篇论文主要关注的是3D异常检测和定位&#xff0c;这是一个在工业质量检查中至关重要的任务。作者们提出了一种新的方法来合成3D异常数据&#x…

三款电容麦的对比

纸面参数 第一款麦克风 灵敏度: -36 dB 2 dB&#xff08;0 dB1V/Pa at 1 kHz&#xff09; 灵敏度较低&#xff0c;需要更高的增益来拾取同样的音量。频率响应: 40 Hz - 18 kHz 响应范围较窄&#xff0c;尤其在高频区域。等效噪音级: ≤18 dB&#xff08;A计权&#xff09; 噪…

easyexcel 导出日期格式化

1.旧版本 在新的版本中formate已经被打上废弃标记。那么不推荐使用这种方式。 2.推荐方式 推荐使用另外一种方式【 Converter 】代码如下&#xff0c;例如需要格式化到毫秒【yyyy-MM-dd HH:mm:ss SSS】级别 创建一个公共Converter import com.alibaba.excel.converters.Conv…

PPT怎样做的更加精美

目录 PPT怎样做的更加精美 3D的GIF图片 3维空间图​编辑 结果有明显的对比 阅读高质量文献,采用他们的图 PPT怎样做的更加精美 3D的GIF图片 3维空间图 结果有明显的对比

插入排序⁻⁻⁻⁻直接插入排序希尔排序

引言 所谓的排序&#xff0c;就是使一串记录按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 常见的排序算法有&#xff1a; 今天我们主要学习插入排序的直接插入排序和希尔排序。 直接插入排序 什么是直接插入排序&#xff1f; 直接插入排序其…

鸿蒙UI开发——亮/暗色模式适配

1、概 述 系统存在深浅色两种显示模式&#xff0c;为了给用户更好的使用体验&#xff0c;应用最好适配暗色和亮色两种模式。从应用与系统配置关联的角度来看&#xff0c;适配暗色和亮色模式可以分为下面两种情况&#xff1a; 应用跟随系统的深浅色模式&#xff1b; 应用主动设…

推荐在线Sql运行

SQL Fiddle 1、网址&#xff1a;SQL Fiddle - Online SQL Compiler for learning & practiceDiscover our free online SQL editor enhanced with AI to chat, explain, and generate code. Support SQL Server, MySQL, MariaDB, PostgreSQL, and SQLite.http://www.sqlfi…

在Ubuntu-22.04 [WSL2]中配置Docker

文章目录 0. 进入Ubuntu-22.041. 更新系统软件包2. 安装Docker相关依赖包3. 添加Docker官方GPG密钥4. 添加Docker软件源5. 安装Docker Engine5.1 更新软件包列表5.2 安装Docker相关软件包 6. 验证Docker安装是否成功6.1 查看Docker版本信息6.2 启动Docker6.3 配置镜像加速器6.4…

AI大模型ollama结合Open-webui

AI大模型Ollama结合Open-webui 作者:行癫(盗版必究) 一:认识 Ollama 1.什么是Ollama ​ Ollama是一个开源的 LLM(大型语言模型)服务工具,用于简化在本地运行大语言模型,降低使用大语言模型的门槛,使得大模型的开发者、研究人员和爱好者能够在本地环境快速实验、管理和…

使用ensp搭建内外互通,使用路由跨不同vlan通信。

1.网络拓扑图 2.规则 &#xff08;1&#xff09;允许 &#xff08;自己&#xff09;ping通内外网&#xff0c;内外网随便一个pc就可以. &#xff08;2&#xff09; 允许&#xff08;电信&#xff09;ping通内外网&#xff0c;内外网随便一个pc就可以 &#xff08;时间问题不做…

gRPC 快速入门 — SpringBoot 实现(1)

目录 一、什么是 RPC 框架 &#xff1f; 二、什么是 gRPC 框架 &#xff1f; 三、传统 RPC 与 gRPC 对比 四、gRPC 的优势和适用场景 五、gRPC 在分布式系统中应用场景 六、什么是 Protocol Buffers&#xff08;ProtoBuf&#xff09;&#xff1f; 特点 使用场景 简单的…

Python实现BBS论坛自动签到【steamtools论坛】

一、知识点分析 1.requests模块介绍 ‌requests模块是Python中用于发送HTTP请求的一个库,它封装了urllib3库,提供了更加便捷的API接口。‌ 通过使用requests模块,用户可以模拟浏览器的请求,发送HTTP请求到指定的URL,并获取响应内容。与urllib相比,requests模块的API更加…