Flutter项目实战之女装商城------商品分类模块实现之数据模型、model测试、分类Provide、编写一级分类界面、首页分类导航处理、编写二级分类界面

继续接着上一次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上运行瞅一下:

嗯,两个平台的效果差不多。

posted on 2021-09-21 06:57  cexo  阅读(306)  评论(1编辑  收藏  举报

导航