Flutter:解析JSON

看到有同窗想要代码,这个在原文中都有。我也正在开发一个app,代码比原文里的还要接近实际使用。android

准备

若是能够的话仍是请看原文。我这都按照个人理解翻译的,仅供参考。json

若是一个App界面上什么都没有的话,那么绝对够无聊的。可是你的app从哪里能够得到有趣的内容呢?必须是网络了。你的,你公司的后端或者是网络上的公开API!后端

不少的网站提供了REST API,通常只要注册就可使用他们的API。在本文中会用到一个瞄星人的站点,你会在那里注册。并把数据展现在一个Flutter app里。在这个站点的API里你会得到一个喵星人的列表,每一个item里面还有一些数据以及喵星人的图片。api

JSON:是JavaScript Object Notation的缩写,基本上全部的API都在用这个数据格式。在本文中你会学到如何把JSON串解析成一个model类的对象,如何把它显示在屏幕上。详细内容包括:数组

  • 调用网络API
  • 解析JSON数据
  • 把数据显示在一个ListView里。
  • 显示网络图片

开始

本教程的代码在这里,点击下载材料获取。bash

本文会使用Android Studio和Flutter插件来开发。你也可使用Visual Studio Code,IntelliJ IDEA来开发。要给Android Studio装Flutter插件,找到Plugins网络

点击“市场”,找到Flutter,以后点击安装按钮。安装了这个plugin以后也就安装了dart plugin了。若是没有自动安装的话,手动安装一下。app

这些plugin都装好以后,重启android studio。在开始界面上选择打开一个已经存在的Android Studio项目而后找到下载好的代码的根目录:异步


Android Studio会弹出一个框获取项目路里用到的包。继续,以后你会看到:async

全部的准备工做完成以后,在设备下拉表里选择一个iOS模拟器或者android模拟器,固然若是你用的是mac,并且Xcode也安装了的话。点击运行按钮。

开始项目就会运行起来,在iOS模拟器是这样的:

Android模拟器是这样的:

理解UI

如今屏幕上尚未任何的数据。接下来就要把数据加上去。

breed, 这里是指猫的品种。

打开lib/screens/cat_breeds.dart文件你会看到以下的代码:

import 'package:flutter/material.dart';
import 'cat_info.dart';

class CatBreedsPage extends StatefulWidget {
  // 1
  CatBreedsPage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _CatBreedsPageState createState() => _CatBreedsPageState();
}
class _CatBreedsPageState extends State<CatBreedsPage> {
  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        // 2
        title: Text(widget.title),
      ),
      // 3
      body: ListView.builder(
          // 4
          itemCount: 0,
          itemBuilder: (context, index) {
            // 5
            return GestureDetector(
              onTap: () {
                Navigator.push(context, MaterialPageRoute(builder: (context) {
                  return CatInfo(catId: 'id', catBreed: 'Name');
                }));
              },
              // 6
              child: Card(
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  // 7
                  child: ListTile(
                    title: Text('Breed Name'),
                    subtitle: Text('Breed Description'),
                  ),
                ),
              ),
            );
          }),
    );
  }
}

解释:

  1. 构建一个CatBreedsPage
  2. AppBar里设置title字段的值
  3. ListView.builder做为字段的方法
  4. count设置为0,毕竟如今尚未任何的数据
  5. 使用Navigator类跳转到CatInfo详情页
  6. 建立一个Card,里面放一个Padding
  7. 添加一个包含title和descriptin的ListTile

使用REST API

请求REST API的时候有不一样的method,通常是GET:用来获取数据,也有POST来保存数据,PATCHPUT用来更新数据。另外还有个DELETE method,用来删除数据。

若是你看过喵星人API,你就会你可使用的请求method。若是你点开Search by Breed链接,你会发现须要一个API key才能用这些API。

注册Cats API

跳转到这里注册一个帐号。这一步是必须的,不然的话那些API都无法调用。

调用网络API

请求API在dart来讲是一件很轻松的事。你只须要使用开始项目里的HTTP库。打开lib/api/network.dart文件,代码是这样的:

// 1
import 'package:http/http.dart';

class Network {
  final String url;
  //2
  Network(this.url);

  // 3
  Future getData() async {
    print('Calling uri: $url');
    // 4
    Response response = await get(url);
    // 5
    if (response.statusCode == 200) {
      // 6
      return response.body;
    } else {
      print(response.statusCode);
    }
  }
}

解释以下:

  1. 引入HTTP库
  2. Network类有一个接受一个字符串为参数的构造函数
  3. 包含了一个异步方法getData()
  4. 使用HTTP GET method请求url,并等待返回
  5. 检查状态码,若是是200那么返回是OK的,不然就是一个Error
  6. 返回结果

理解JSON

JSON大多数REST API返回的数据的格式。另一个通用格式是XML。

JSON实例:

{
  "user": {
      "name": "Kevin Moore",
      "occupation": "Programmer"
   }
}

上面的例子是从一个{大括号开始,说明是一个对象数据。JSON也能够是一个数组,这时候通常是[开头。JSON格式不能出错,也就是又开始就须要有结束,当它是一个对象的时候就须要有结束的大括号}。

在服务端返回了一个JSON串以后,你能够:

  1. 从字符串里得到key/value对
  2. 把字符串转化为一个dart的Map对象,再从里面拿到key/value对。
  3. 把字符串转化为一个model类的对象,而后从这个对象的属性里得到数据

以上方法均可以获取到数据。可是最好的方法仍是把JSON串转化为model类的对象。

解析JSON

咱们来看看有哪些方法能够用来解析JSON。

手动解析

你可使用dart:convert库来解析:

import 'dart:convert';

Map<String, dynamic> user = jsonDecode(jsonString);
var name = user['user]['name'];

这看起来没什么难的。可是若是处理复杂的JSON,代码就会变的冗长繁复。

使用库

pub.dev,能够找处处理JSON的Flutter库:

  • HTTP用来处理网络请求。
  • json_annotation用来给你的model类添加注解

你会发现两个工具库能够建立把json串转化为model对象的工具方法:

  • build_runner,运行json_serializable库
  • json_serializable,建立额外的把json串转化为model对象的工具代码

这俩个库要放在pubspec.yaml文件的dev_dependencies后面。

最后,在pubspec.yaml文件的dependencies里:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.2
  http: ^0.12.0+2
  json_annotation: ^2.0.0

dev_dependencies里:

dev_dependencies:
  flutter_test:
    sdk: flutter    
  build_runner: ^1.0.0
  json_serializable: ^2.0.0

接下来点击在android studio的右上角出现的Packages get。咱们须要的包就回下载下来了。

Cat API

打开lib/api/cats_api.dart文件。第一行就是叫作apiKey的字符串常量。用你本身的key放进去。

const String apiKey = 'Your Key';
//1
const String catAPIURL = 'https://api.thecatapi.com/v1/breeds?';
// 2
const String catImageAPIURL = 'https://api.thecatapi.com/v1/images/search?';
// 3
const String breedString = 'breed_id=';
// 4
const String apiKeyString = 'x-api-key=$apiKey';

class CatAPI {
  // 5
  Future<dynamic> getCatBreeds() async {
     // 6
    Network network = Network('$catAPIURL$apiKeyString');
    // 7
    var catData = await network.getData();
    return catData;
  }
  // 8
  Future<dynamic> getCatBreed(String breedName) async {
    Network network =
        Network('$catImageAPIURL$breedString$breedName&$apiKeyString');
    var catData = await network.getData();
    return catData;
  }
}

解释以下:

  1. API的url字符串,获取喵星人列表
  2. 一样是url串,查找喵星人的图
  3. 获取一个breed ID
  4. 存放你的key的字符串
  5. 获取列表的方法getCatBreeds()
  6. Network类,须要传入API的url和你的key组成的字符串
  7. 获取某个breed的喵的图片的方法getCatBreed(String breedName)

使用Cat API

在文件lib/screens/cat_breeds.dart文件中的_CatBreedsPageState,里添加以下代码:

void getCatData() async {
  var result = await CatAPI().getCatBreeds();
  print(result);
}

这个方法会得到喵的breeds。

这里须要从cat_info.dart文件引入CatAPI类。你能够手动实现,也能够把光标移到CatAPI上,而后使用快捷键Option+Enter,选择Import

接下来在initState()方法下面添加这个方法:

getCatData();

如今能够运行代码了,你应该能够发现请求API获得的JSON串了:

建立Model类

在Models目录下打开cats.dart文件。你会发现注释掉的JSON串。

添加一个描述喵种的类

class Breed {
  String id;
  String name;
  String description;
  String temperament;

  Breed({this.id, this.name, this.description, this.temperament});
}

这个类的字段基本上和从API里得到的数据的字段一致。id用来获取喵种的图片,namedescription用来在列表的CardView中显示。

喵种的列表数据就是从[开始,到]结束的一个JSON数组。

class BreedList {
  List<Breed> breeds;

  BreedList({this.breeds});
}

这个类里面包含了breeds列表。

要查找喵种的图片,还须要cat的model类,cat breed的model类以及cat breed列表的model类。添加下面的代码到cats.dart文件里:

class Cat {
  String name;
  String description;
  String life_span;

  Cat({this.name, this.description, this.life_span});
}

class CatBreed {
  String id;
  String url;
  int width;
  int height;
  List<Cat> breeds;

  CatBreed({this.id, this.url, this.width, this.height, this.breeds});
}

class CatList {
  List<CatBreed> breeds;

  CatList({this.breeds});
}

在教程的app里是不须要用到temperamentlife_span字段的,固然你能够用这两个字段丰富app的功能。

使用JSON注解

如今可使用json_annotation库来把数据解析到model类的对象里。

cats.dart文件里添加以下代码:

import 'package:json_annotation/json_annotation.dart';
part 'cats.g.dart';

part语句容许你引入一个文件,并使用里面的私有变量。如今会显示一个错误。可是用build_runner文件cats.g.dart以后就不会了。

如今给每一个model类添加注解:@JsonSerializable()。如:

@JsonSerializable()
class Breed {
  String id;
  String name;
  String description;
  String temperament;

  Breed({this.id, this.name, this.description, this.temperament});
}

JSON转换方法

在这一节,咱们会给每一个类都加上一个工厂方法。build runner会根据这些方法生成专门处理数据转化的代码。

Breed类的构造函数后面添加以下代码:

factory Breed.fromJson(Map<String, dynamic> json) => _$BreedFromJson(json);

Map<String, dynamic> toJson() => _$BreedToJson(this);

每一个类都会包含一个fromJson和一个toJson()方法,这两个方法会调用生成的数据转换方法。如今Android Stuido里会显示一些错误,暂时忽略。

BreedList类的构造函数后面添加以下代码:

factory BreedList.fromJson(List<dynamic> json) {
  return BreedList(
      breeds: json
          .map((e) => Breed.fromJson(e as Map<String, dynamic>))
          .toList());
}

Cat类里添加fromJsontoJson两个方法:

factory Cat.fromJson(Map<String, dynamic> json) => _$CatFromJson(json);

Map<String, dynamic> toJson() => _$CatToJson(this);

最后在CatList类的构造函数后面添加以下代码:

factory CatList.fromJson(List<dynamic> json) {
  return CatList(
      breeds: json
          .map((e) => CatBreed.fromJson(e as Map<String, dynamic>))
          .toList());
}

使用Build Runner

在项目的终端里运行以下的命令:

flutter packages pub run build_runner build

若是没什么问题就会生出出来咱们前文反复提到的cats.g.dart文件。

使用Model类

如今Nodel类都已经完备了,能够用起来了。

开始前,如今_CatBreedsPageState里添加一个属性:

class _CatBreedsPageState extends State<CatBreedsPage> {  
  BreedList breedList = BreedList(); //<-添加这个属性
  ...

引入
引入cats.dart文件。添加dart:convert
getCatData()方法里,在print语句后面添加以下的代码:

// 1
var catMap = json.decode(result);
// 2 
setState(() {
  // 3
  breedList = BreedList.fromJson(catMap);
});

解释以下:

  1. 使用json.decode(result)从JSON串获得一个map
  2. 调用setState来重绘Widget,由于数据已经发生了变化
  3. 使用fromJson(catMap)把获得的map转化为一个喵种的列表

接下来就该把数据显示在界面里了。

跳转到body: ListView.builder语句,把itemCount:0替换为:

itemCount: (breedList == null || breedList.breeds == null || breedList.breeds.length == 0) ? 0 : breedList.breeds.length,

这样就会把itemCount设置为实际数量的item值。

ListTile里的titlesubTitle替换为以下的代码:

title: Text(breedList.breeds[index].name),
subtitle: Text(breedList.breeds[index].description),

如今运行代码,会出现以下的界面了:

祝贺你!!!

建立详情页

下一步添加onTap,在用户点击了一行的时候能够跳转到详情页,而且显示出这个喵种的图片。使用下面的代码替换onTap里的代码:

return CatInfo(catId: breedList.breeds[index].id, catBreed: breedList.breeds[index].name);

这样就把点击那行的idname都传到了CatInfo的构造函数里。

如今打开lib/screens/cat_info.dart文件在_CatInfoState类的initState上面添加下面的代码:

CatList catList = CatList();

void getCatData() async {
  var catJson = await CatAPI().getCatBreed(widget.catId);
  print(catJson);

  var catMap = json.decode(catJson);
  print(catMap);
  setState(() {
    catList = CatList.fromJson(catMap);
  });
}

initState方法里添加对getCatData()的调用。

@override
void initState() {
  super.initState();
  getCatData();
}

确保import了全部须要的文件。

getCat()方法里, 在mediaSize属性声明的后面添加以下代码:

if (catList == null ||
      catList.breeds == null ||
      catList.breeds.length == 0) {
    return Container();
  }

这会返回一个空的Container,若是API请求返回的数据为空的话。在height参数后面添加以下的代码:

// 1
decoration: BoxDecoration(image: DecorationImage(
  // 2
  image: NetworkImage(catList.breeds[0].url),fit: BoxFit.contain,
)),

解释以下:

  1. BoxDecoration,让你能够在Box里画一张图
  2. NetworkImage,能够经过url加载一张图片

注意,你是要用一个装饰器来显示一张图片,你什么都不用作。只要把url放进NetworkImage里就能够,很酷对吧。

运行代码,你会看到这样的界面:

相关文章
相关标签/搜索