Flutter入門並開發天氣預報APP(7)——Http網絡請求、Json轉Dart實體類及異步更新UI

相關Demo源碼可見a1203991686/CoolWeather_Flutter

1. Flutter Http 網絡請求

Flutter網絡請求可分爲兩種方式,一種爲Dart:IO庫中爲我們提供的HttpClient,另一種爲Dart第三方庫Dio

1.1 HttpClient

HttpClientDart:IO庫自帶的一個類,通過他來實現網絡請求最底層、也可以完完全全的自定義設置,但是相對於其他人封裝好的第三方庫來說顯得複雜。

引入

導入Dart:IO庫:

import 'dart:io';

創建一個HttpClient:

 HttpClient httpClient = new HttpClient();

創建一個Uri

如果你是Http請求:

var uri = new Uri.http(
    host,
    queryParameters: {
        "xx":"xx",
        "yy":"dd"
    }
);

如果你是Https請求:

var uri = new Uri.https(
    host,
    queryParameters: {
        "xx":"xx",
        "yy":"dd"
    }
);

根據uri獲取返回數據

HttpClientRequest request = await httpClient.getUrl(uri);
HttpClientResponse response = await request.close();

讀取內容

進行這一步得導入dart:convert庫:

import 'dart:convert';

... //省略中間代碼 

String responseBody = await response.transform(Utf8Decoder()).join();

print(respinseBody)

最後關閉Client

httpClient.close();

1.2 Dio

Dio是Dart社區上別人上傳的第三方庫,他封裝了HttpClient,相較來說更爲簡單、方便。

引入

在項目的pubspec.yaml文件的dependencies中導入Dio

dependencies:
  dio: 
  // 如果冒號後面不帶具體版本信息則表示自動下載最新版

接着在需要使用的代碼文件裏面,import導入他:

import 'package:dio/dio.dart';

接着我們就可以使用Dio了。

示例

首先得創建一個Dio對象。

Dio dio =  Dio();

發起GET請求:

Response response;
response=await dio.get(uri)
print(response.data.toString());

發起POST請求:

response=await dio.post(uri);

2. Json轉Dart

手動生成Dart實體類

首先得大家安利一個網站,因爲Dart實體類比Java實體類多了幾個方法,所以相對來說麻煩,通過這個網站就可以自動生成json數據對應的實體類:
https://caijinglong.github.io/json2dart/
json2dart

比方說我們使用這個鏈接(http://guolin.tech/api/china),並讓他自動轉dart:
Province

接下來只需要創建dart文件,然後再把代碼複製進去就行了。

不出意外,代碼肯定會報錯。
dart實體類

報錯信息爲:

error: Target of URI hasn't been generated: 'province.g.dart'. (uri_has_not_been_generated at [cool_weather] lib/bean/province.dart:3)

error: The method '_$ProvinceFromJson' isn't defined for the class 'Province'. (undefined_method at [cool_weather] lib/bean/province.dart:31)

error: The method '_$ProvinceToJson' isn't defined for the class 'Province'. (undefined_method at [cool_weather] lib/bean/province.dart:33)

這是因爲我們項目下還沒有province.g.dart這個文件,這個文件是根據dart實體類自動生成的,那麼我們該怎麼生成他呢?

這個時候我們需要在pubspec.yaml文件中導入三個依賴包:
轉dart

我們重點只需要管三個寫着需要導入的。輸入之後點擊pubspec.yaml文件右上角的packages get,就會自動下載包了。

然後在終端中,轉到項目根目錄下,輸入

flutter packages pub run build_runner build

接着你就會驚喜的發現,在你的dart實體類下面多了一個文件,也就是你所缺失的~.g.dart文件,並且實體類中那些報錯也都沒了。
dart.g.dart

將請求回來的Response轉爲Dart實體類

接着HttpClient返回來的responseBody或者Dio返回的response

List<Province> provinceList;
provinceList = ProvinceList.getProvinceList(jsonResponse);

provinceList就是返回來的數據轉成的實體類的對象了。

3. 異步更新UI

在此處介紹兩種方法,第一種是我自己想出來的沙雕方法,第二種是通過Flutter提供的專門用於異步更新UI的組件FutureBuilder。

沙雕方法

在此說一下我這個方法的想法。

大家可以回顧下我之前講Widget的時候說過,Widget有一個方法initState可以用來加載UI,以及一個setState方法可用來提醒Flutter重新加載UI。

說到這很多同學一定想到了,我們可以在獲取到數據之後通過setState方法來更新UI。

獲取到數據

這個不同多說,上面講的全都是獲取數據。

設置一個空的實體類

此處我還是拿上面的Province.dart來做例子。我們正常情況下獲取到的數據轉成的實體類的對象是List<Province> provinceList。所以我們此處先設置一個空的對象:List<Province> _provinceList

接着在initState裏面調用網絡請求的方法:

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

然後在getProvince方法裏面獲取數據,並將數據provinceList傳給_provinceList:

void getProvince() async {
    var response = await Dio().get("http://guolin.tech/api/china");
    provinceList = ProvinceList.getProvinceList(response);
    setState(() {
        _provinces = provinceList;
    });
}

這樣就可以當獲取到數據的時候就通知Flutter更新狀態了。這個時候有的同學可能會問我,你這個也只是獲取到數據的時候才更新啊,那當還沒獲取到數據的時候呢?

這塊就體現到了爲什麼我們要設置一個_provinces了。

我們完全可以在build方法設置下,判斷下_provinces爲不爲空:如果爲空,就證明沒數據,就加載其它頁面;如果不爲空,就證明有數據了,那就加載數據。

body: _provinces == null
          ? new Text("正在請求")
          : new ListView.builder(
          ... //省去代碼,總的來說就是將_provinces的數據加載ListView裏面,一個ListView的基本構造方法
          )

FutureBuilder

迴歸正題,我上面那個方法真的是有夠沙雕的。

那我們來看看這個名正言順的Flutter親生兒子FutureBuilder。

首先我們看下定義:

FutureBuilder({
  this.future,
  this.initialData,
  @required this.builder,
})
  • future:異步任務;
  • initialData:初始化數據;
  • builder:builder方法。

示例

body: FutureBuilder(
    future: Dio().get("http://guolin.tech/api/china"),
    builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.done) {
            Response response = snapshot.data;
            print(response.toString());
            //發生錯誤
            if (snapshot.hasError) {
                return Text(snapshot.error.toString());
            }

            provinceList = ProvinceList.fromJson(response.data);
            //請求成功,通過項目信息構建用於顯示項目名稱的ListView
            return ListView.builder(
                itemCount: provinceList.provinces.length,
                itemBuilder: (context, index) {
                    return GestureDetector(
                        child: ListTile(
                            title: Text("${provinceList.provinces[index].name}"),
                        ),
                        onTap: () {
                            Navigator.push(context,
                                MaterialPageRoute(builder: (context) {
                                    return CityPageWidget(
                                        cityID: provinceList.provinces[index].id);
                                }));
                        },
                    );
                },
            );
        }
        // 請求未完成時彈出loading
        return CircularProgressIndicator();
    },
)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章