1202 年了,還不了解 Flutter 解析數(shù)據(jù)的來(lái)康康

2021年了, Flutter 2.0版本已經(jīng)發(fā)布,來(lái)康康Flutter 的Model 是怎么解析json 數(shù)據(jù)的吧!
如果你請(qǐng)求到豆瓣電影的高分電影排行榜json數(shù)據(jù)格式入下, 讓你用flutter做成一個(gè)電影排行榜的列表, 你該如何下手呢?

[
  {
   "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": ["美國(guó)"],
   "title": "肖申克的救贖",
   "url": "https:\/\/movie.douban.com\/subject\/1292052\/",
   "release_date": "1994-09-10",
   "actor_count": 25,
   "vote_count": 2290289,
   "score": "9.7",
   "actors": ["蒂姆·羅賓斯", "摩根·弗里曼", "鮑勃·岡頓", "威廉姆·賽德勒", "克蘭西·布朗", "吉爾·貝羅斯", "馬克·羅斯頓", "詹姆斯·惠特摩", "杰弗里·德曼", "拉里·布蘭登伯格", "尼爾·吉恩托利", "布賴恩·利比", "大衛(wèi)·普羅瓦爾", "約瑟夫·勞格諾", "祖德·塞克利拉", "保羅·麥克蘭尼", "芮妮·布萊恩", "阿方索·弗里曼", "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": ["劇情", "愛(ài)情", "同性"],
   "regions": ["中國(guó)大陸", "中國(guó)香港"],
   "title": "霸王別姬",
   "url": "https:\/\/movie.douban.com\/subject\/1291546\/",
   "release_date": "1993-07-26",
   "actor_count": 26,
   "vote_count": 1698938,
   "score": "9.6",
   "actors": ["張國(guó)榮", "張豐毅", "鞏俐", "葛優(yōu)", "英達(dá)", "蔣雯麗", "吳大維", "呂齊", "雷漢", "尹治", "馬明威", "費(fèi)振翔", "智一桐", "李春", "趙海龍", "李丹", "童弟", "沈慧芬", "黃斐", "徐杰", "黃磊", "馮遠(yuǎn)征", "楊立新", "方征", "周璞", "隋永清"],
   "is_watched": false
   }
]

首先登場(chǎng)的是 json_serializable 真心好用的一個(gè)json 解析框架, 項(xiàng)目的pubspec.yaml 中添加 json_serializable:

dev_dependencies:
  flutter_test:
    sdk: flutter
  json_serializable: ^3.5.1

然后, 觀察這個(gè)josn 的數(shù)據(jù)結(jié)構(gòu),發(fā)現(xiàn)是數(shù)組套字典;
那么model 的數(shù)據(jù)結(jié)構(gòu)為:

 "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": ["劇情", "愛(ài)情", "同性"],
   "regions": ["中國(guó)大陸", "中國(guó)香港"],
   "title": "霸王別姬",
   "url": "https:\/\/movie.douban.com\/subject\/1291546\/",
   "release_date": "1993-07-26",
   "actor_count": 26,
   "vote_count": 1698938,
   "score": "9.6",
   "actors": ["張國(guó)榮", "張豐毅", "鞏俐", "葛優(yōu)", "英達(dá)", "蔣雯麗", "吳大維", "呂齊", "雷漢", "尹治", "馬明威", "費(fèi)振翔", "智一桐", "李春", "趙海龍", "李丹", "童弟", "沈慧芬", "黃斐", "徐杰", "黃磊", "馮遠(yuǎn)征", "楊立新", "方征", "周璞", "隋永清"],
   "is_watched": false,

使用這個(gè) json_serializable 自動(dòng)轉(zhuǎn)model 工具:
https://caijinglong.github.io/json2dart/index_ch.html

然后把這個(gè)工具生成的model 代碼copy 到項(xiàng)目的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);

}

項(xiàng)目的根目錄中,執(zhí)行:

flutter packages pub run build_runner build

如果報(bào)錯(cuò):

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

此時(shí),項(xiàng)目中出現(xiàn) douban.g.dart 的文件, 此時(shí)項(xiàng)目報(bào)錯(cuò)消失.

// 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,
    };

這個(gè)文件禁止手寫.

OK,準(zhǔn)備工作已經(jīng)搞完了.
創(chuàng)建一個(gè)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 的數(shù)量
          itemCount: subjects.length,
          itemBuilder: (BuildContext context, int index) {
            return GestureDetector(//Flutter 手勢(shì)處理
              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: () {
                //監(jiān)聽(tīng)點(diǎn)擊事件
                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,
    );
  }

  // 電影標(biāo)題,星標(biāo)評(píng)分展哭,演員簡(jiǎn)介
  Widget _getMovieInfoView() {
    return Container(
      height: itemHeight,
      alignment: Alignment.topLeft,
      child: Column(
        children: <Widget>[
          // 電影名字 年份
          _getTitleView(douban),
          // 星星評(píng)分
          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)),
                    ),
                  )
    );
  }
}

// 電影星星評(píng)分/等級(jí)
// ignore: must_be_immutable
class RatingBar extends StatelessWidget {

  double star;
  RatingBar(this.star);

  @override
  Widget build(BuildContext context) {
    List<Widget> _startList = [];
    // 實(shí)心星星(整除挺据,余數(shù)部分舍棄取整)
    var _startNumber = star ~/ 2;
    // 半實(shí)心星星
    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,
      ),
    );
  }
}

完畢!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秃臣,一起剝皮案震驚了整個(gè)濱河市者冤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茵汰,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孽鸡,死亡現(xiàn)場(chǎng)離奇詭異蹂午,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)彬碱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門豆胸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人巷疼,你說(shuō)我怎么就攤上這事晚胡。” “怎么了嚼沿?”我有些...
    開(kāi)封第一講書人閱讀 153,443評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵估盘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我骡尽,道長(zhǎng)忿檩,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,475評(píng)論 1 279
  • 正文 為了忘掉前任爆阶,我火速辦了婚禮燥透,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辨图。我一直安慰自己班套,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,458評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布故河。 她就那樣靜靜地躺著吱韭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上理盆,一...
    開(kāi)封第一講書人閱讀 49,185評(píng)論 1 284
  • 那天痘煤,我揣著相機(jī)與錄音,去河邊找鬼猿规。 笑死衷快,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的姨俩。 我是一名探鬼主播蘸拔,決...
    沈念sama閱讀 38,451評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼环葵!你這毒婦竟也來(lái)了调窍?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,112評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤张遭,失蹤者是張志新(化名)和其女友劉穎邓萨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體菊卷,經(jīng)...
    沈念sama閱讀 43,609評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡先誉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,083評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了的烁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片褐耳。...
    茶點(diǎn)故事閱讀 38,163評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖渴庆,靈堂內(nèi)的尸體忽然破棺而出铃芦,到底是詐尸還是另有隱情,我是刑警寧澤襟雷,帶...
    沈念sama閱讀 33,803評(píng)論 4 323
  • 正文 年R本政府宣布刃滓,位于F島的核電站,受9級(jí)特大地震影響耸弄,放射性物質(zhì)發(fā)生泄漏咧虎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,357評(píng)論 3 307
  • 文/蒙蒙 一计呈、第九天 我趴在偏房一處隱蔽的房頂上張望砰诵。 院中可真熱鬧,春花似錦捌显、人聲如沸茁彭。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,357評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)理肺。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妹萨,已是汗流浹背年枕。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,590評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乎完,地道東北人熏兄。 一個(gè)月前我還...
    沈念sama閱讀 45,636評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像囱怕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子毫别,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,925評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容