Nealyang / PersonalBlog

:memo: Nealyang personal blog

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

“flutter”数据model及json处理

Nealyang opened this issue · comments

前言

由于我们最终是需要通过接口获取数据的,笔者个人习惯,比较喜欢先确认了字段再去进行代码的编写,所以这一章节,我们先mock下接口的数据。

从Chrome中,我copy了一份请求:list api

我们将数据copy一份到本地json中

在项目的根目录下新建一个 assets 文件夹,用于存放我们的json

  • assets/indexListData.json

内容较长,这里就不粘贴了。大家可以直接copy我的文件,也可以直接copy请求过来的数据

json

因为是资源文件的注入,所以我们需要在pubspec.yaml中注册一下

  assets:
   - assets/indexListData.json

listCell数据模型

原始数据我们有了,根据UI,我们肯定需要将list的每一个cell拆出来作为组件来使用的。

所以我们在lib目录下新建一个widgets目录用于存放我们项目中需要自定的组件

分析cell的UI样式
cell

我们来定义一个该Cell需要的数据model!

在lib目录下新建model目录

  • lib/model/indexCell.dart
  import '../util/util.dart';
  
  class IndexCell {
    bool hot;
    String isCollection;
    String tag;
    String username;
    int collectionCount;
    int commentCount;
    String title;
    String createdTime;
    String detailUrl;
  
    IndexCell(
        {this.hot,
        this.tag,
        this.username,
        this.collectionCount,
        this.createdTime,
        this.commentCount,
        this.title,
        this.detailUrl,
        this.isCollection});
  
    factory IndexCell.fromJson(Map<String, dynamic> json) {
      return IndexCell(
        hot: json['hot'],
        collectionCount: json['collectionCount'],
        commentCount: json['commentsCount'],
        tag: json['tags'][0]['title'] + '/' + json['category']['name'],
        username: json['user']['username'],
        createdTime: Util.getTimeDuration(json['createdAt']),
        title: json['title'],
        detailUrl: json['originalUrl'],
        isCollection: json['type'] ,
      );
    }
  }

如上,我们就定义了一个包含一些字段的类,因为涉及使用量很大,我们使用一个工厂构造函数,为了方便传json,这里我们再定义了一个命名构造函数 IndexCell.fromJson,而里面是对接口字段的处理赋值操作。

因为是mock(接口)过来的数据,很多时候我们都要进行一些数据格式或者字段的处理,方便我们前端UI的展示,所以这里我们在lib目录下新建一个util目录

  • lib/util/util.dart
  class Util {
  
    static String getTimeDuration(String comTime) {
      var nowTime = DateTime.now();
      var compareTime = DateTime.parse(comTime);
      if (nowTime.isAfter(compareTime)) {
        if (nowTime.year == compareTime.year) {
          if (nowTime.month == compareTime.month) {
            if (nowTime.day == compareTime.day) {
              if (nowTime.hour == compareTime.hour) {
                if (nowTime.minute == compareTime.minute) {
                  return '片刻之间';
                }
                return (nowTime.minute - compareTime.minute).toString() + '分钟前';
              }
              return (nowTime.hour - compareTime.hour).toString() + '小时前';
            }
            return (nowTime.day - compareTime.day).toString() + '天前';
          }
          return (nowTime.month - compareTime.month).toString() + '月前';
        }
        return (nowTime.year - compareTime.year).toString() + '年前';
      }
      return 'time error';
    }
  }

上面代码写的有点呆呆的,后来我查了DateTime对象,可以使用difference方法来对比两个时间差,这里就不做修改了

我们如上定义了一个处理时间的方法,复制给cell

使用mock数据和数据model

这里说下笔者个人的代码习惯,如上代码,indexPage 是我们首页UI的容器,我只想在这里一个方法拿到我要的数据,然后丢给cell去渲染,就完事了,不希望有太多关于数据的逻辑处理

所以我们在lib/util下新建一个文件 dataUtils.dart文件,用于对请求过来数据的处理和封装

  • lib/util/dataUtils.dart
    引入基础库
  import 'dart:convert';
  import '../model/indexCell.dart';
  import 'package:flutter/services.dart' show rootBundle;
  import 'dart:async' show Future;

services、async 用于我们的数据请求,虽然是读取本地json但是熟悉node应该都明白,IO当然也是异步操作。

convert用于对json数据的处理,强烈推荐文章:[译]在 Flutter 中解析复杂的
JSON

引入数据model,方便处理和吐出。

  class DataUtils {
    static Future<String> _loadIndexListAsset() async {
      return await rootBundle.loadString('assets/indexListData.json');
    }
  
    static Future<List<IndexCell>> getIndexListData() async {
      List<IndexCell> resultList = new List();
      String jsonString = await _loadIndexListAsset();
      final jsonResponseList = json.decode(jsonString)['indexListData'];
      for(int i = 0;i<jsonResponseList.length;i++){
        // resultList.add();
        IndexCell cellData = new IndexCell.fromJson(jsonResponseList[i]);
        resultList.add(cellData);
      }
      return resultList;
    }
  }

关于上面的语法已在第一章节重点说明,这里不再赘述。
方法getIndexListData吐出List<IndexCell>

在IndexPage中,我们直接如下使用

  • lib/pages/indexPage.dart
  getList(bool isLoadMore) {
    DataUtils.getIndexListData().then((resultList) {
      setState(() {
        _listData = resultList;
      });
    });
  }

方法如上,调用时机这里我们方便测试,放到initState中,并将拿到的数据丢给我们的Cell widget来测试下,是否成功了(当然,使用print就可以),注意这里 传参:isLoadMore 是为了后面做加载更多的时候使用,这里我们重点在于mock data,所以先留下这个flag,暂时方法体里并未使用该变量。

  @override
  void initState() {
    super.initState();
    getList(false);
  }
  
    @override
  Widget build(BuildContext context) {
    print(_listData.length);
    return Column(
      children: <Widget>[
        Text('IndexPage'),
        IndexListCell(cellInfo: _listData.length>0?_listData[0]:new Map())
      ],
    );
  }

上面的三目运算看起来也是呆呆的哇,没有数据就不应该渲染IndexListCell嘛,但是这里我们先这样,后面我们会加loading

cell 中对于数据Model的使用

  • widgets/indexListCell.dart
  import 'package:flutter/material.dart';
  import '../model/indexCell.dart';
  
  class IndexListCell extends StatelessWidget {
    final IndexCell cellInfo;
  
    IndexListCell({Key key,this.cellInfo}):super(key : key);
  
    @override
    Widget build(BuildContext context) {
      return Container(
        child: Text(cellInfo.username),
      );
    }
  }

如上,此刻的你,应该看到如下界面:
data

congratulation~ 成功了!

数据已经有了,下面就开始编写我们的这个indexListCellwidget吧!

总结

如上,我们完成了本地mock的数据,至此,你应该学会

  • 定义和使用数据模型
  • 异步获取本地mock data

我这里提示 type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'IndexCell'

我这里提示 type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'IndexCell'

数据转换类型错误了吧,可以 debugger 看下哪部分转换出错了

我这里提示 type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'IndexCell'

数据转换类型错误了吧,可以 debugger 看下哪部分转换出错了

应该是因为new Map()不能隐式转换成IndexCell的原因。

tags有时候会是空的,空的时候直接取值的话会throw,getIndexListData()好像会直接返回空对象