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裏就能夠,很酷對吧。

運行代碼,你會看到這樣的界面:

相關文章
相關標籤/搜索