继续接着上一次https://www.cnblogs.com/webor2006/p/14956298.html的Flutter项目往下学习,在上一次是开始对分类界面的数据已经准备好了,整体APP的样子回忆一下:
其中目前分类的数据请求已经准备好了:
接下来则根据数据接口来实现界面的搭建了,这个页面的逻辑稍复杂一点,不着急,慢慢来实现。
分类数据模型:
首先得要先根据接口的JSON数据来定义咱们的VO实体模型,先将分类的JSON结构贴出来:
{ "code":"0", "message":"success", "data":[ { "firstCategoryId": "1", "firstCategoryName": "毛衣", "secondCategoryVO": [{ "secondCategoryId": "11", "firstCategoryId": "1", "secondCategoryName": "羊绒", "comments": "" } ], "comments": null, "image": "http://192.168.31.192:3000/images/category/1.png" }, { "firstCategoryId": "2", "firstCategoryName": "西服", "secondCategoryVO": [{ "secondCategoryId": "21", "firstCategoryId": "2", "secondCategoryName": "小西服", "comments": "" }, { "secondCategoryId": "22", "firstCategoryId": "2", "secondCategoryName": "职业装", "comments": "" } ], "comments": null, "image": "http://192.168.31.192:3000/images/category/1.png" } ] }
新建文件:
先新建一个实体文件:
根据json生成实体:
对于Android开发,通常对于json对应的实体都会采用插件来生成,通常是JsonFomat:
那,在Flutter开发中,有木有类似的自动帮忙生成实体的方式呢?这里没有比较好集成在Android Studio中的插件,在网上搜了个在线的:https://jsontodart.com/,也还行,咱们试一下:
还不错,将其拷到咱们工程中来:
class CategoryModel { String code; String message; List<Data> data; CategoryModel({this.code, this.message, this.data}); CategoryModel.fromJson(Map<String, dynamic> json) { code = json['code']; message = json['message']; if (json['data'] != null) { data = new List<Data>(); json['data'].forEach((v) { data.add(new Data.fromJson(v)); }); } } Map<String, dynamic> toJson() { final Map<String, dynamic> data = new Map<String, dynamic>(); data['code'] = this.code; data['message'] = this.message; if (this.data != null) { data['data'] = this.data.map((v) => v.toJson()).toList(); } return data; } } class Data { String firstCategoryId; String firstCategoryName; List<SecondCategoryVO> secondCategoryVO; Null comments; String image; Data( {this.firstCategoryId, this.firstCategoryName, this.secondCategoryVO, this.comments, this.image}); Data.fromJson(Map<String, dynamic> json) { firstCategoryId = json['firstCategoryId']; firstCategoryName = json['firstCategoryName']; if (json['secondCategoryVO'] != null) { secondCategoryVO = new List<SecondCategoryVO>(); json['secondCategoryVO'].forEach((v) { secondCategoryVO.add(new SecondCategoryVO.fromJson(v)); }); } comments = json['comments']; image = json['image']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = new Map<String, dynamic>(); data['firstCategoryId'] = this.firstCategoryId; data['firstCategoryName'] = this.firstCategoryName; if (this.secondCategoryVO != null) { data['secondCategoryVO'] = this.secondCategoryVO.map((v) => v.toJson()).toList(); } data['comments'] = this.comments; data['image'] = this.image; return data; } } class SecondCategoryVO { String secondCategoryId; String firstCategoryId; String secondCategoryName; String comments; SecondCategoryVO( {this.secondCategoryId, this.firstCategoryId, this.secondCategoryName, this.comments}); SecondCategoryVO.fromJson(Map<String, dynamic> json) { secondCategoryId = json['secondCategoryId']; firstCategoryId = json['firstCategoryId']; secondCategoryName = json['secondCategoryName']; comments = json['comments']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = new Map<String, dynamic>(); data['secondCategoryId'] = this.secondCategoryId; data['firstCategoryId'] = this.firstCategoryId; data['secondCategoryName'] = this.secondCategoryName; data['comments'] = this.comments; return data; } }
整个代码没啥好解释的,不过由于Flutter的学习也间隔有一段时间了,对其中一些感受生疏的语法再复习:
关于这个,其实在之前就已经解释过,可以翻阅它https://www.cnblogs.com/webor2006/p/14410445.html,语法生疏了也只能多复习复习:
另外这是啥语法呢?
命名构造,具体可以回忆https://www.cnblogs.com/webor2006/p/11981709.html:
model测试:
好,接下来咱们回到分类页面把请求结果转换成实体来测试一下看是否model写得有问题。
此时debug看一下请求的数据是否成功的解析成model了:
分类Provide:
现在数据和实体都已经准备就绪了,是不是就可以开始进行界面的编写,是的,但是呢,这里在界面编写之前,最好还是先来将Provide准备好,还记得Provide是干嘛的么?其实在之前首页tab时就已经用过了:
那还记得它的使用么?这里简单回忆一下,当时是在处理底部tab切换时来使用的:
如果对Provide不太了解的可以参考https://www.cnblogs.com/webor2006/p/13576296.html,那对于分类这个模块来说,Provide它发挥的作用场景主要是有:单击一级分类需要显示它下面的二级分类、改变子分类的索引、加载更多数据、首页分类点击跳转到相应的分类,现在说的场景可能不一定你能提前感知到,待之后慢慢实现时再来体会Provide它的作用。
创建分类Provide:
接下来定义一些需要用到的变量,目前先不用过多管这些变量的意思,等用到时自然就明白了:
import 'package:flutter/material.dart'; import 'package:fluttershop/model/category_model.dart'; //分类Provide class CategoryProvider with ChangeNotifier { List<SecondCategoryVO> secondCategoryList = []; //二级分类列表 int secondCategoryIndex = 0; //二级分类索引 int firstCategoryIndex = 0; //一级分类索引 String firstCategoryId = '4'; //二级ID String secondCategoryId = ''; //一级ID int page = 1; //列表页数, 当改变一级分类或者二级分类时进行改变 String noMoreText = ''; //显示更多的表示 bool isNewCategory = true; }
当你创建了一个Provide之后,记得要对它进行一个注册哟,也就是这块:
改变分类索引Provide:
首页分类跳转点击索引处理:
这是指这种场景:
针对这种场景,咱们先在Provide中增加对应的处理方法:
点击一级分类索引处理:
接下来再来定义一个针对点击一级分类处理的Provide的方法:
分类page索引处理:
接下来再增加一个对于页码的处理方法:
获得二级分类Provide:
接下来处理获取二级分类数据的逻辑处理,直接贴出来代码,因为也比较好理解:
最后,再增加两个改变状态的逻辑,比较简单:
关于Provide部分,暂且先写到这,待之后如有需要再进行完善。
编写一级分类界面:
界面先用Provide包裹:
我们定义的Provide肯定是需要用到界面上的,如之前首页的使用,要想监听Provide,则需要在组件最外层这样包裹一下:
所以,咱们来编写一下:
搭建一级分类视图:
1、定义宽度:
其中为啥要使用ScreenUtil不太清楚的可以参考https://www.cnblogs.com/webor2006/p/13576296.html,其实就是为了屏幕适配。
2、增加右边线:
接着给整个左侧一级菜单栏增加一下右边框:
3、构建一级分类列表:
接下来则来构建整个的列表:
4、运行:
5、列表条目封装:
目前咱们列表的条目是用TextView来占了一个位,接下来对其进行进一步的封装:
高度和间距设置:
处理当前点中状态:
接下来处理点中与不点中的状态:
首先得要有判断是否是当前选中状态的条件对吧,很简单:
接下来根据是否点击状态来设置一下装饰器的样式:
设置文本:
接下来设置一下条目的文本:
运行:
此时再运行看一下效果:
嗯,基本的样子有了,只是目前还没有做点击事件的处理。
点击一级分类处理:
接下来则来处理一级分类的点击事件处理,此时就得通过Provide了,如下:
此时,点击效果就有了,如下:
为啥这么调用一个Provide,监听效果就有了呢?因为咱们在这块已经做了Provide状态监听了:
而在调用方法中,有改变firstCategoryIndex值的状态:
所以这也就是使用Provide它的价值,进行状态管理非常方便。
首页分类导航处理:
在继续编写分类页面的逻辑之前,先来处理一个首页分类导航的问题,对于首页不是有这么一个区域么?
其实它就是对应的咱们目前写的分类页面,点击应该跳到指定分类的索引位置,有了Provide的封装其实也非常简单,回到首页这块的点击事件处:
这个注释写得有点问题,应该是跳到分类页面,而不是详情,这里就直接实现了:
此时只改变了分类的索引,还没有进行Tab的切换,所以还需要做一步处理,关于Tab的切换还有印象么,也是调用跟Tab状态切换的Provide,如下:
运行看一下:
而之所有Tab状态可以进行改变,也是在这个页面做状态监听了:
编写二级分类界面:
接下来则来处理一级分类点击之后二级分类的展示了,也就是这块:
数据获取:
首先是在点击左侧一级分类时,需要获取二级分类的数据,这里也是调一下Provide相关方法既可,如下:
其中,首页分类的点击也得加一下:
其中有个报错就是需要获取二级分类的数据,此时就只能进行接口请求查询了,所以很显然是一个异步的,具体如下:
界面搭建:
1、定义宽高及边框样式:
这里又是由Provide进行包裹对吧,因为它也需要监听页面的状态。
2、列表构建:
3、运行看一下初步效果:
4、构建列表项:
接下来则具体构建一下列表条目,这块比较简单,代码直接给出:
Widget _rightInkWel(int index, SecondCategoryVO item) { bool isClick = false; isClick = (index == Provide.value<CategoryProvider>(context).secondCategoryIndex) ? true : false; return InkWell( onTap: () { Provide.value<CategoryProvider>(context) .changeSecondIndex(index, item.secondCategoryId); //TODO 获取商品列表 }, child: Container( padding: EdgeInsets.fromLTRB(5.0, 10.0, 5.0, 10.0), child: Text( item.secondCategoryName, style: TextStyle( fontSize: ScreenUtil().setSp(28), color: isClick ? KColor.primaryColor : Colors.black, ), ), ), ); } }
运行:
好,接下来运行一下:
IOS运行:
接下来分类页面就只剩分类商品列表显示功能了,这块下次继续,最后,将目前实现的效果在IOS上运行瞅一下:
嗯,两个平台的效果差不多。