继续接着上一次Flutter项目实战之女装商城------火爆专区实现、商品分类数据准备的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的学习也间隔有一段时间了,对其中一些感受生疏的语法再复习:
关于这个,其实在之前就已经解释过,可以翻阅它博文阅读密码验证 - 博客园,语法生疏了也只能多复习复习:
另外这是啥语法呢?
命名构造,具体可以回忆Fluter基础巩固之Dart语言详解<二> - cexo - 博客园:
model测试:
好,接下来咱们回到分类页面把请求结果转换成实体来测试一下看是否model写得有问题。
此时debug看一下请求的数据是否成功的解析成model了:
分类Provide:
现在数据和实体都已经准备就绪了,是不是就可以开始进行界面的编写,是的,但是呢,这里在界面编写之前,最好还是先来将Provide准备好,还记得Provide是干嘛的么?其实在之前首页tab时就已经用过了:
那还记得它的使用么?这里简单回忆一下,当时是在处理底部tab切换时来使用的:
如果对Provide不太了解的可以参考Flutter项目实战之女装商城------导航栏添加、状态管理、屏幕适配 密码保护,那对于分类这个模块来说,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不太清楚的可以参考Flutter项目实战之女装商城------导航栏添加、状态管理、屏幕适配 密码保护,其实就是为了屏幕适配。
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上运行瞅一下:
嗯,两个平台的效果差不多。