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

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 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 types;

  @JsonKey(name: 'regions')
  List 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 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 srcJson) => _$DoubanFromJson(srcJson);

  Map 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 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 _$DoubanToJson(Douban instance) => {
      '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 createState() {
    return _DouBanState();
  }
}

class _DouBanState extends State {

  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: [
                                  // 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: [
          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: [
          // 图片加载
          _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: [
          // 电影名字 年份
          _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 actors = douban.actors;
    var stringBuffer = StringBuffer();
    // 种类
    List 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 _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 解析数据的来康康)