美文网首页
1202 年了,还不了解 Flutter 解析数据的来康康

1202 年了,还不了解 Flutter 解析数据的来康康

作者: zbzbwxe | 来源:发表于2021-03-12 23:18 被阅读0次

    2021年了, Flutter 2.0版本已经发布,来康康Flutter 的Model 是怎么解析json 数据的吧!
    如果你请求到豆瓣电影的高分电影排行榜json数据格式入下, 让你用flutter做成一个电影排行榜的列表, 你该如何下手呢?

    [
      {
       "rating": ["9.7", "50"],
       "rank": 1,
       "cover_url": "https://img2.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p480747492.jpg",
       "is_playable": true,
       "id": "1292052",
       "types": ["犯罪", "剧情"],
       "regions": ["美国"],
       "title": "肖申克的救赎",
       "url": "https:\/\/movie.douban.com\/subject\/1292052\/",
       "release_date": "1994-09-10",
       "actor_count": 25,
       "vote_count": 2290289,
       "score": "9.7",
       "actors": ["蒂姆·罗宾斯", "摩根·弗里曼", "鲍勃·冈顿", "威廉姆·赛德勒", "克兰西·布朗", "吉尔·贝罗斯", "马克·罗斯顿", "詹姆斯·惠特摩", "杰弗里·德曼", "拉里·布兰登伯格", "尼尔·吉恩托利", "布赖恩·利比", "大卫·普罗瓦尔", "约瑟夫·劳格诺", "祖德·塞克利拉", "保罗·麦克兰尼", "芮妮·布莱恩", "阿方索·弗里曼", "V·J·福斯特", "弗兰克·梅德拉诺", "马克·迈尔斯", "尼尔·萨默斯", "耐德·巴拉米", "布赖恩·戴拉特", "唐·麦克马纳斯"],
       "is_watched": false
       }, {
       "rating": ["9.6", "50"],
       "rank": 2,
       "cover_url": "https://img3.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2561716440.jpg",
       "is_playable": true,
       "id": "1291546",
       "types": ["剧情", "爱情", "同性"],
       "regions": ["中国大陆", "中国香港"],
       "title": "霸王别姬",
       "url": "https:\/\/movie.douban.com\/subject\/1291546\/",
       "release_date": "1993-07-26",
       "actor_count": 26,
       "vote_count": 1698938,
       "score": "9.6",
       "actors": ["张国荣", "张丰毅", "巩俐", "葛优", "英达", "蒋雯丽", "吴大维", "吕齐", "雷汉", "尹治", "马明威", "费振翔", "智一桐", "李春", "赵海龙", "李丹", "童弟", "沈慧芬", "黄斐", "徐杰", "黄磊", "冯远征", "杨立新", "方征", "周璞", "隋永清"],
       "is_watched": false
       }
    ]
    

    首先登场的是 json_serializable 真心好用的一个json 解析框架, 项目的pubspec.yaml 中添加 json_serializable:

    dev_dependencies:
      flutter_test:
        sdk: flutter
      json_serializable: ^3.5.1
    

    然后, 观察这个josn 的数据结构,发现是数组套字典;
    那么model 的数据结构为:

     "rating": ["9.6", "50"],
       "rank": 2,
       "cover_url": "https://img3.doubanio.com\/view\/photo\/s_ratio_poster\/public\/p2561716440.jpg",
       "is_playable": true,
       "id": "1291546",
       "types": ["剧情", "爱情", "同性"],
       "regions": ["中国大陆", "中国香港"],
       "title": "霸王别姬",
       "url": "https:\/\/movie.douban.com\/subject\/1291546\/",
       "release_date": "1993-07-26",
       "actor_count": 26,
       "vote_count": 1698938,
       "score": "9.6",
       "actors": ["张国荣", "张丰毅", "巩俐", "葛优", "英达", "蒋雯丽", "吴大维", "吕齐", "雷汉", "尹治", "马明威", "费振翔", "智一桐", "李春", "赵海龙", "李丹", "童弟", "沈慧芬", "黄斐", "徐杰", "黄磊", "冯远征", "杨立新", "方征", "周璞", "隋永清"],
       "is_watched": false,
    

    使用这个 json_serializable 自动转model 工具:
    https://caijinglong.github.io/json2dart/index_ch.html

    然后把这个工具生成的model 代码copy 到项目的douban.dart model 中,

    import 'package:json_annotation/json_annotation.dart';
    
    part 'douban.g.dart';
    
    @JsonSerializable()
    class Douban extends Object {
    
      @JsonKey(name: 'rating')
      List<String> rating;
    
      @JsonKey(name: 'rank')
      int rank;
    
      @JsonKey(name: 'cover_url')
      String coverUrl;
    
      @JsonKey(name: 'is_playable')
      bool isPlayable;
    
      @JsonKey(name: 'id')
      String id;
    
      @JsonKey(name: 'types')
      List<String> types;
    
      @JsonKey(name: 'regions')
      List<String> regions;
    
      @JsonKey(name: 'title')
      String title;
    
      @JsonKey(name: 'url')
      String url;
    
      @JsonKey(name: 'release_date')
      String releaseDate;
    
      @JsonKey(name: 'actor_count')
      int actorCount;
    
      @JsonKey(name: 'vote_count')
      int voteCount;
    
      @JsonKey(name: 'score')
      String score;
    
      @JsonKey(name: 'actors')
      List<String> actors;
    
      @JsonKey(name: 'is_watched')
      bool isWatched;
    
      Douban(this.rating,
            this.rank,
            this.coverUrl,
            this.isPlayable,
            this.id,
            this.types,
            this.regions,
            this.title,
            this.url,
            this.releaseDate,
            this.actorCount,
            this.voteCount,
            this.score,
            this.actors,
            this.isWatched,
          );
    
      factory Douban.fromJson(Map<String, dynamic> srcJson) => _$DoubanFromJson(srcJson);
    
      Map<String, dynamic> toJson() => _$DoubanToJson(this);
    
    }
    
    

    项目的根目录中,执行:

    flutter packages pub run build_runner build
    

    如果报错:

    flutter packages pub run build_runner clean
    flutter packages pub run build_runner build --delete-conflicting-outputs
    

    此时,项目中出现 douban.g.dart 的文件, 此时项目报错消失.

    // GENERATED CODE - DO NOT MODIFY BY HAND
    
    part of 'douban.dart';
    
    // **************************************************************************
    // JsonSerializableGenerator
    // **************************************************************************
    
    Douban _$DoubanFromJson(Map<String, dynamic> json) {
      return Douban(
        (json['rating'] as List)?.map((e) => e as String)?.toList(),
        json['rank'] as int,
        json['cover_url'] as String,
        json['is_playable'] as bool,
        json['id'] as String,
        (json['types'] as List)?.map((e) => e as String)?.toList(),
        (json['regions'] as List)?.map((e) => e as String)?.toList(),
        json['title'] as String,
        json['url'] as String,
        json['release_date'] as String,
        json['actor_count'] as int,
        json['vote_count'] as int,
        json['score'] as String,
        (json['actors'] as List)?.map((e) => e as String)?.toList(),
        json['is_watched'] as bool,
      );
    }
    
    Map<String, dynamic> _$DoubanToJson(Douban instance) => <String, dynamic>{
          'rating': instance.rating,
          'rank': instance.rank,
          'cover_url': instance.coverUrl,
          'is_playable': instance.isPlayable,
          'id': instance.id,
          'types': instance.types,
          'regions': instance.regions,
          'title': instance.title,
          'url': instance.url,
          'release_date': instance.releaseDate,
          'actor_count': instance.actorCount,
          'vote_count': instance.voteCount,
          'score': instance.score,
          'actors': instance.actors,
          'is_watched': instance.isWatched,
        };
    
    

    这个文件禁止手写.

    OK,准备工作已经搞完了.
    创建一个douban_listview.dart 的文件,写入如下代码:

    import 'package:flutter/material.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter_navigation_demo/json_resource/douban_json.dart';
    import 'package:flutter_navigation_demo/widgets/desc_widget.dart';
    import 'package:flutter_navigation_demo/model/douban.dart';
    
    class DouBanListView extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _DouBanState();
      }
    }
    
    class _DouBanState extends State<DouBanListView> {
    
      List subjects = [];
    
      double itemHeight = 180.0;
    
      Douban douban;
    
      @override
      void initState() {
        setState(() {
          subjects = movieDataList;
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: _getListViewContainer(),
        );
      }
    
      Widget _getListViewContainer() {
        if (subjects.length == null || subjects.length == 0) {
          // loading
          return Center(child: Text('null'),);
        }
        return
          ListView.builder(
              //item 的数量
              itemCount: subjects.length,
              itemBuilder: (BuildContext context, int index) {
                return GestureDetector(//Flutter 手势处理
                  child: Container(
                            color: Colors.transparent,
                            child:
                              Column(crossAxisAlignment: CrossAxisAlignment.start,
                                    children: <Widget>[
                                      // rank
                                      _movieRankWidget(index + 1),
    
                                      _getItemContainerView(subjects[index]),
    
                                      //下面的灰色分割线
                                      Container(
                                        height: 0.5,
                                        margin: EdgeInsets.fromLTRB(5, 0, 5, 5),
                                        color: Color.fromARGB(255, 234, 233, 234),
                                      ),
    
                                    ],
                                  ),
                  ),
                  onTap: () {
                    //监听点击事件
                    print("click item index=$index");
                  },
                );
              });
      }
    
      // 肖申克的救赎(1993) View
      Widget _getTitleView(subject) {
        var title = douban.title;
        var releaseDate =douban.releaseDate;
        return Container(
          child: Row(
            children: <Widget>[
              Icon(
                Icons.play_circle_outline,
                color: Colors.redAccent,
              ),
              Text(
                title,
                style: TextStyle(
                    fontSize: 16, fontWeight: FontWeight.bold, color: Colors.black),
              ),
              Text('($releaseDate)',
                  style: TextStyle(
                      fontSize: 14,
                      fontWeight: FontWeight.bold,
                      color: Colors.grey))
            ],
          ),
        );
      }
    
      // 电影图片 介绍
      Widget _getItemContainerView(Map subject) {
       douban = Douban.fromJson(subject);
    
        return Container(
          width: double.infinity,
          padding: EdgeInsets.all(5.0),
          child: Row(
            children: <Widget>[
              // 图片加载
              _getNetworkImage(douban.coverUrl),
              Expanded(
                child:
                // 电影信息
                _getMovieInfoView(),
                flex: 1,
              )
            ],
          ),
        );
      }
    
      // 圆角图片
      _getNetworkImage(String imageUrl) {
        // CachedNetworkImage();
        return Container(
          decoration: BoxDecoration(
              image:
              DecorationImage(image: NetworkImage(imageUrl), fit: BoxFit.cover),
              borderRadius: BorderRadius.all(Radius.circular(5.0))),
              margin: EdgeInsets.only(left: 8, top: 2, right: 4, bottom: 4),
              height: itemHeight,
              width: 100.0,
        );
      }
    
      // 电影标题,星标评分,演员简介
      Widget _getMovieInfoView() {
        return Container(
          height: itemHeight,
          alignment: Alignment.topLeft,
          child: Column(
            children: <Widget>[
              // 电影名字 年份
              _getTitleView(douban),
              // 星星评分
              RatingBar(double.parse(douban.score)),
              // 演员名字介绍
              DescWidget(douban),
            ],
          ),
        );
      }
    
      // 电影排行榜(列表排行)
      Widget _movieRankWidget(int rank) {
        return Container(
          child: Text(
            'No.$rank',
            style: TextStyle(color: Color.fromARGB(255, 133, 66, 0)),
          ),
          // 装饰(金色)
          decoration: BoxDecoration(
              color: Color.fromARGB(255, 255, 201, 129),
              borderRadius: BorderRadius.all(Radius.circular(5.0))),
          padding: EdgeInsets.fromLTRB(8, 4, 8, 4),
          margin: EdgeInsets.only(left: 12, top: 10),
        );
      }
    }
    

    封装的组件类:
    desc_widget.dart

    
    import 'package:flutter/material.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:flutter_navigation_demo/model/douban.dart';
    
    // 类别、演员、介绍
    class DescWidget extends StatelessWidget {
    
      Douban douban;
    
      DescWidget(this.douban);
    
      @override
      Widget build(BuildContext context) {
    
        List<String> actors = douban.actors;
        var stringBuffer = StringBuffer();
        // 种类
        List<String> types = douban.types;
    
        if(types.length != 0) {
          for (var i = 0; i < types.length; i++) {
            stringBuffer.write('${types[i]}');
            stringBuffer.write('/');
          }
        }
    
          for (var i = 0; i < actors.length; i++) {
          stringBuffer.write('${actors[i]}');
          stringBuffer.write('/');
          }
      
        String movieInfo = stringBuffer.toString();
        return
          Flexible(child:
                      Container(
                        alignment: Alignment.topLeft,
                        child: Text(
                                  movieInfo,
                                  softWrap: true,
                                  textDirection: TextDirection.ltr,
                                  style:
                                  TextStyle(fontSize: 16, color: Color.fromARGB(255, 118, 117, 118)),
                        ),
                      )
        );
      }
    }
    
    // 电影星星评分/等级
    // ignore: must_be_immutable
    class RatingBar extends StatelessWidget {
    
      double star;
      RatingBar(this.star);
    
      @override
      Widget build(BuildContext context) {
        List<Widget> _startList = [];
        // 实心星星(整除,余数部分舍弃取整)
        var _startNumber = star ~/ 2;
        // 半实心星星
        var _startHalf = 0;
        if (star.toString().contains('.')) {
          int tmp = int.parse((star.toString().split('.')[1]));
          if (tmp >= 5) {
            _startHalf = 1;
          }
        }
        // 空心星星
        var _startEmpty = 5 - _startNumber - _startHalf;
    
        for (var i = 0; i < _startNumber; i++) {
          _startList.add(Icon(
            Icons.star,
            color: Colors.amberAccent,
            size: 18,
          ));
        }
    
        if (_startHalf > 0) {
          _startList.add(Icon(
            Icons.star_half,
            color: Colors.amberAccent,
            size: 18,
          ));
        }
    
        for (var i = 0; i < _startEmpty; i++) {
          _startList.add(Icon(
            Icons.star_border,
            color: Colors.grey,
            size: 18,
          ));
        }
    
        _startList.add(Text(
          '$star',
          style: TextStyle(
            color: Colors.grey,
          ),
        ));
    
        return Container(
          alignment: Alignment.topLeft,
          padding: const EdgeInsets.only(left: 0, top: 8, right: 0, bottom: 5),
          child: Row(
            children: _startList,
          ),
        );
      }
    }
    
    

    完毕!

    相关文章

      网友评论

          本文标题:1202 年了,还不了解 Flutter 解析数据的来康康

          本文链接:https://www.haomeiwen.com/subject/ychtcltx.html