Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品

本文主要是介绍Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

时间飞逝,伴随着打工人春节的结束心也得跟着回收一下,新年新气象,今年也没有什么高大上的目标,暂且先把去年落下的各种课程的学习给完整结束了,不过计划还是会再多学点其它新的东东,到时看情况,年后的第一次学习从之前落下的Flutter开始。

距离上一次Flutter项目实战之女装商城------首页设计分析、数据准备、Dio请求处理、接口配置、请求首页数据 - cexo - 博客园的学习已经时隔4个月之久了,先来回忆一下当时APP的样子做到了哪了:

其实要注意,后台数据是用的node来搭建的,注意本地先npm start一下:

然后本机ip会随时变动,记得根据本机当前的ip,将代码修改一下,比如:

基本也就搭了个架子,接下来则从首页开始继续往下撸。

添加轮播图:

效果:

接下来先来搭建一下首页的商品轮播图,样子如下:

添加依赖:

要实现这样的效果,当然得借助于三方库喽,这里使用flutter_swiper,官方地址:flutter_swiper | Flutter Package,

先来在工程中添加依赖:

构建轮播组件:

1、导包:

先来对home_page的代码进行一个全局回顾,目前长这样子:

import 'dart:convert';import 'package:flutter/material.dart';
import 'package:flutter_easyrefresh/easy_refresh.dart';
import 'package:fluttershop/config/color.dart';
import 'package:fluttershop/config/string.dart';
import 'package:fluttershop/service/http_service.dart';
import 'package:flutter_swiper/flutter_swiper.dart';class HomePage extends StatefulWidget {@override_HomePageState createState() => _HomePageState();
}class _HomePageState extends State<HomePage>with AutomaticKeepAliveClientMixin {//这里只是做一个演示,其实是不用加这个的,因为咱们的BottomNavigationBar是由IndexedStack构建的,具体参考index_page.dart文件@overridebool get wantKeepAlive => true;GlobalKey<ClassicalFooterWidgetState> _footerKey =GlobalKey<ClassicalFooterWidgetState>();@overridevoid initState() {super.initState();print('home_page.initState()');}@overrideWidget build(BuildContext context) {print('home_page.build()');return Scaffold(backgroundColor: Color.fromRGBO(244, 245, 245, 1.0),appBar: AppBar(title: Text(KString.homeTitle),centerTitle: true,),body: FutureBuilder(future: request('homePageContext', formData: null),builder: (context, snapshot) {if (snapshot.hasData) {var data = json.decode(snapshot.data.toString());print("gethomePageContext data:${data}");var dataList = data['data'];List<Map> swiperDataList =(dataList['slides'] as List).cast(); //轮播图List<Map> navigatorList =(dataList['category'] as List).cast(); //分类List<Map> recommendList =(dataList['recommend'] as List).cast(); //商品推荐List<Map> floor1 = (dataList['floor1'] as List).cast(); //底部商品推荐Map fp1 = dataList['floor1Pic']; //广告return EasyRefresh.custom(enableControlFinishRefresh: false,enableControlFinishLoad: true,footer: ClassicalFooter(key: _footerKey,bgColor: Colors.white,textColor: KColor.refreshTextColor,infoColor: KColor.refreshTextColor,noMoreText: '',//加载中...loadingText: KString.loading,loadReadyText: KString.loadReadyText,),slivers: [SliverList(delegate: SliverChildBuilderDelegate((context, index) {return Container(width: 60.0,height: 60.0,child: Center(child: Text('$index'),),color: index % 2 == 0? Colors.grey[300]: Colors.transparent,);},childCount: 5,),),],onLoad: () async {print('开始加载更多');//TODO 执行下一页的请求},);} else {return Center(child: Text('加载中...'),);}}),);}
}

此时需要用ListView来构建整个界面的内容,所以代码需要修改一下:

2、编写Swiper组件框架:

其中注意Dart的语法,我在编写这块时都感觉完全生疏了,时间一长工作中又不用铁定健忘,这时之前学习留下的笔记就发挥它的威力了,这里说的Dart语法就是构造函数相关,一个是:

关于它可以回忆Fluter基础巩固之Dart语言详解<二> - cexo - 博客园,

另一个是在它定义构造时用了一个大括号:

这又是啥语法呢?可选命名参数,如果忘了可以参考Fluter基础巩固之Dart语言详解<一> - cexo - 博客园,如下:

3、具体编写Swiper组件:

接下来具体编写Swpier组件的构建,先来参考官方的DEMO:

这里就一气呵成了,只对关键地方做下说明:

其中关于fit属性有很多种,这里参考大佬的Flutter中 BoxFit 的几种样式说明,贴出来为我所用:

BoxFit.contain

将会尽可能的伸展来达到组件的边缘。

Flutter中 BoxFit 的几种样式

BoxFit.cover

将会尽可能小的放大来铺满整个组件

Flutter中 BoxFit 的几种样式

BoxFit.fill

通过拉伸(改变原图比例)来铺满组件

Flutter中 BoxFit 的几种样式

BoxFit.fitHeight

保持原图比例,拉升高度来达到组件的高度

Flutter中 BoxFit 的几种样式

BoxFit.fitWidth

保持原图比例,拉升宽度来达到组件的宽度

Flutter中 BoxFit 的几种样式

BoxFit.none

正常展示,默认居中。

Flutter中 BoxFit 的几种样式

BoxFit.scaleDown

保持在组件中并且不改变比例

Flutter中 BoxFit 的几种样式

另外还有一个关于设置尺寸的代码:

这块是屏幕适配的写法,记得不要直接写死一个值,关于这块可以回忆博文阅读密码验证 - 博客园,不过多解释。

然后需要设置里面小红点和自动轮播,很简单:

4、运行:

呃,咋图片没加载出来空白了呢?这里要注意了【以后这块就不强调了】!!!然后看一下每个图片的url是这么个形式:

因为node服务是运行在电脑上的,127.0.0.1代表的是电脑的本机ip,但是!!!我运行是在真机上,而非电脑上开设的模拟器,那很显然在手机上是无法通过127.0.0.1这个电脑端的本地地址来访问图片了,解决办法也很简单,直接用电脑的局域网ip既可,查看一下电脑的ip:

然后看一下目前咱们node的baseurl的ip是:

当然这个ip可能随着网络的变化会不断变化,所以以后在学习之前一定先要确保ip地址是正确的,再重启一下npm,然后再运行就ok了:

效果如下:

嗯,不错~~整体也比较简单。

分类导航:

效果:

其中图标都是后台返的,另外最多就支持10个分类,如果超过10个则需要截取。

具体实现:

1、列表中增加一个widget:

2、使用GridView来构建:

对于分类这种网络布局可以使用Flutter自带的GridView来进行构建,关于它的用法可以参考开启Fluter基础之旅<二>-------Future再论、常用组件、Material Design风格组件学习 - cexo - 博客园,下面来构建一下:

这块比较简单,其中标红的在设置大小时都用到了EdgeInsets这个类,这个在之前的学习中也大量用到了,看一下这个类的官方解释:

总之就是对于方便给一个元素设置左右上下的间隙,而它比较经典的方法有Flutter之EdgeInsets_u013095264的博客-CSDN博客:

关于这块要记得熟练运用,另外对于GridView的这个属性也稍加说明一下:

其中physics是Flutter中的动画的一个种类,回忆一下开启Fluter基础之旅<四>-------表格、动画、手势 - cexo - 博客园:

而常见的physics有很多,看一下它的子类便可以了解:

这块就点一下,可以在之后慢慢了解。

接下来则来设置一下GridView的item项:

那如何构建呢?如下:

接下来就来构建一下列表项,由于它也是需要可以进行点击的,所以依然用InkWell,比较简单,贴出来:

Widget _gridViewItemUI(BuildContext context, item, index) {return InkWell(onTap: () {//TODO 商品分类点击进详情},child: Column(children: [Image.network(item['image'],width: ScreenUtil().setWidth(95),),Text(item['firstCategoryName']),],),);}

接下来运行一下,看有木有问题,发现报错了。。

看IDE报错的详情:

其实是犯了一个低级错,啥错呢?

再运行一下:

商品推荐:

效果:

 当然是支持左右滑动了。

具体实现:

1、列表中增加一个widget:

//商品推荐组件
class RecommendUI extends StatelessWidget {final List<Map> recommendList;RecommendUI({Key key, this.recommendList}) : super(key: key);@overrideWidget build(BuildContext context) {return null;}
}

2、准备推荐商品标题widget:

先来准备子widget,布局也就是按从局部到整体的思路一步步来进行,也就是推荐的标题,如下:

其中涉及到几个色值和字符串,提到公共的地方为:

3、准备推荐商品列表widget:

目前先简单显示一个文本,先将整个界面架子组拼起来。

4、将子widget组拼起来:

接下来整体看一下基本架子的样子:

5、准备列表item的widget:

//列表itemWidget _item(context, index) {return InkWell(onTap: () {//TODO 商品点击进详情},child: Container(width: ScreenUtil().setWidth(200),padding: EdgeInsets.all(0.0),decoration: BoxDecoration(color: Colors.white,border: Border(left: BorderSide(width: 0.5, color: KColor.defaultBorderColor),)),child: Column(children: [Image.network(recommendList[index]['image']),//打折价格Text('¥${recommendList[index]['presentPrice']}',style: TextStyle(color: KColor.presentPriceTextColor),),//原价Text('¥${recommendList[index]['oriPrice']}',style: KFont.oriPriceStyle,),],),),);

其中配置相关的属性如下:

其中对于有中划线的原价以后在多处页面都可能会用到,所以这里对这样的样式字体进行一个抽取:

接下来运行看一下效果:

细节调整:

细节一:防止溢出处理

看这张图:

其实这里加一个防溢出的代码既可,如下:

再运行:

看一下它的作用:

细节二:图片压缩调整

目前对于这张图很不和谐:

此时就需要给图片增加压缩属性:

再看效果:

貌似上下木有间隙,看着有点别扭,因为有处笔误写错了:

修改一下:

再来看一下:

关于Image的几种压缩效果自己一个个试就成,不需要记,做得多了也就知道其它的意思了,就类似于Android的ImageView控件一样,它的缩放方式也很多,实际中还得根据产品的需求自己去尝试调试,具体这块的属性可以参考:Flutter中Image的fit属性解析 - 简书。

广告位处理:

效果:

就是一张纯图,没啥逻辑。

具体实现: 

1、准备商品广告widget:

这块比较简单,直接贴出:

//商品推荐中间广告
class FloorPic extends StatelessWidget {final Map floorPic;FloorPic({Key key, this.floorPic}) : super(key: key);@overrideWidget build(BuildContext context) {return Container(margin: EdgeInsets.only(top: 10.0),child: InkWell(onTap: () {//TODO 商品点击进详情},child: Image.network(floorPic['PICTURE_ADDRESS'],fit: BoxFit.cover,),));}
}

2、运行:

底部商品:

效果:

貌似这布局有点复杂,其实这里是涉及到row和column的嵌套布局,也是实际项目中会经常用到的一种布局方式,通过这个模块的编写正好可以掌握这种布局方法。

具体实现:

1、列表中增加一个widget:

//商品推荐下层
class Floor extends StatelessWidget {final List<Map> floor;Floor({Key key, this.floor}) : super(key: key);@overrideWidget build(BuildContext context) {return Container();}
}

2、使用Row来搭建左右两列效果:

3、具体构建五大子widget:

先来构建左边上面大图:

比较简单,接下来四个就依葫芦画瓢了,当然间隙需要稍加修改一下,一口气呵成:

//商品推荐下层
class Floor extends StatelessWidget {final List<Map> floor;Floor({Key key, this.floor}) : super(key: key);//跳转到商品详情void jumpDetail(context, String goodId) {//TODO}@overrideWidget build(BuildContext context) {return Container(child: Row(children: [//左侧商品Expanded(child: Column(children: [//左上图Container(padding: EdgeInsets.only(top: 4),height: ScreenUtil().setHeight(400),child: InkWell(onTap: () {jumpDetail(context, floor[0]['goodsId']);},child: Image.network(floor[0]['image'],fit: BoxFit.cover,)),),//左下图Container(padding: EdgeInsets.only(top: 1, right: 1),height: ScreenUtil().setHeight(200),child: InkWell(onTap: () {jumpDetail(context, floor[1]['goodsId']);},child: Image.network(floor[1]['image'],fit: BoxFit.cover,)),),],),),//右侧商品Expanded(child: Column(children: [//右上图Container(padding: EdgeInsets.only(top: 4, left: 1, bottom: 1),height: ScreenUtil().setHeight(200),child: InkWell(onTap: () {jumpDetail(context, floor[2]['goodsId']);},child: Image.network(floor[2]['image'],fit: BoxFit.cover,)),),//右中图Container(padding: EdgeInsets.only(top: 1, left: 1),height: ScreenUtil().setHeight(200),child: InkWell(onTap: () {jumpDetail(context, floor[3]['goodsId']);},child: Image.network(floor[3]['image'],fit: BoxFit.cover,)),),//右下图Container(padding: EdgeInsets.only(top: 1, left: 1),height: ScreenUtil().setHeight(200),child: InkWell(onTap: () {jumpDetail(context, floor[4]['goodsId']);},child: Image.network(floor[4]['image'],fit: BoxFit.cover,)),),],),),],),);}
}

4、给Row设置属性:

最后还需要给Row设置两个属性,如下:

mainAxisAlignment:

其中它有很多选项:

其中只需要对spaceBetween、spaceAround、spaceEvenly进行了解既可,具体啥意思可以参考这位大佬的Flutter学习之MainAxisAlignment属性详解_bkysdc的博客-CSDN博客,介绍还是蛮详细的,其中盗个图:

三者文字简述如下:

  • spaceBetween:

  • spaceAround:

  • spaceEvenly:

对于这些属性的理解还得在以后项目中的使用中来慢慢消化。

mainAxisSize:

其中它也有两个选项:

啥意思呢?目前还是找度娘线性布局Row和Column - 小图教父 - 博客园【关于这些涉及到木有接触过的知识点以后有时间整体单独成篇再细致的研究一下,目前先直接网上一大抄来解决疑惑】,如下:

运行:

最后运行看一下效果:

那ios上运行效果是否也一样呢?这里开一个ios模拟器瞅一下:

嗯,还不错,只不过我电脑有点卡,录屏更卡,所以可以自行真机运行看一下性能怎么样~~

总结:

最后纵观一下对于这次首页功能的编写,发现整体的编写思路都是从局部的widget,再到全局的widget,一点点进行构建,比如拿商品推荐组件来说,它由标题和列表组成,于是乎将整体拆成了2个widget来进行实现:

这样实现起来代码可读性也比较强,而不要将所有的实现全塞到这块:

先学到这,下次继续~~

关注个人公众号,获得实时推送

这篇关于Flutter项目实战之女装商城------添加轮播图、分类导航、商品推荐、广告位处理、底部商品的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/771645

相关文章

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

基于 Cursor 开发 Spring Boot 项目详细攻略

《基于Cursor开发SpringBoot项目详细攻略》Cursor是集成GPT4、Claude3.5等LLM的VSCode类AI编程工具,支持SpringBoot项目开发全流程,涵盖环境配... 目录cursor是什么?基于 Cursor 开发 Spring Boot 项目完整指南1. 环境准备2. 创建

MyBatis分页查询实战案例完整流程

《MyBatis分页查询实战案例完整流程》MyBatis是一个强大的Java持久层框架,支持自定义SQL和高级映射,本案例以员工工资信息管理为例,详细讲解如何在IDEA中使用MyBatis结合Page... 目录1. MyBATis框架简介2. 分页查询原理与应用场景2.1 分页查询的基本原理2.1.1 分

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Python中 try / except / else / finally 异常处理方法详解

《Python中try/except/else/finally异常处理方法详解》:本文主要介绍Python中try/except/else/finally异常处理方法的相关资料,涵... 目录1. 基本结构2. 各部分的作用tryexceptelsefinally3. 执行流程总结4. 常见用法(1)多个e

PHP应用中处理限流和API节流的最佳实践

《PHP应用中处理限流和API节流的最佳实践》限流和API节流对于确保Web应用程序的可靠性、安全性和可扩展性至关重要,本文将详细介绍PHP应用中处理限流和API节流的最佳实践,下面就来和小编一起学习... 目录限流的重要性在 php 中实施限流的最佳实践使用集中式存储进行状态管理(如 Redis)采用滑动

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

MyBatis-plus处理存储json数据过程

《MyBatis-plus处理存储json数据过程》文章介绍MyBatis-Plus3.4.21处理对象与集合的差异:对象可用内置Handler配合autoResultMap,集合需自定义处理器继承F... 目录1、如果是对象2、如果需要转换的是List集合总结对象和集合分两种情况处理,目前我用的MP的版本