• 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上运行瞅一下:

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

  • 相关阅读:
    Gitlab 进首页报错500 Whoops。 访问仓库错误码503
    iptables设置Linux全局代理
    元素到底部位置判断
    行为型设计模式:职责链模式
    Win7SP1安装.net 4.6 4.7 4.8
    深入理解并行编程 电子书 pdf
    CUDA C编程权威指南 第二版 电子书 pdf
    程序员修炼之道 第二版 电子书 pdf
    linux多线程服务端编程 电子书 pdf
    Clean Craftsmanship: Disciplines, Standards, and Ethics 电子书 pdf
  • 原文地址:https://www.cnblogs.com/webor2006/p/15272239.html
Copyright © 2020-2023  润新知