相关Demo源码可见Github a1203991686/CoolWeather_Flutter
Flutter Http 网络请求
Flutter网络请求可分为两种方式,一种为Dart:IO库中为我们提供的HttpClient,另一种为Dart第三方库Dio。
HttpClient
HttpClient是Dart:IO库自带的一个类,通过他来实现网络请求最底层、也可以完完全全的自定义设置,但是相对于其他人封装好的第三方库来说显得复杂。
引入
导入Dart:IO库:
创建一个HttpClient:
| 1
 |  HttpClient httpClient = new HttpClient();
 | 
创建一个Uri
如果你是Http请求:
| 1
2
3
4
5
6
7
 | var uri = new Uri.http(
    host,
    queryParameters: {
        "xx":"xx",
        "yy":"dd"
    }
);
 | 
如果你是Https请求:
| 1
2
3
4
5
6
7
 | var uri = new Uri.https(
    host,
    queryParameters: {
        "xx":"xx",
        "yy":"dd"
    }
);
 | 
根据uri获取返回数据
| 1
2
 | HttpClientRequest request = await httpClient.getUrl(uri);
HttpClientResponse response = await request.close();
 | 
读取内容
进行这一步得导入dart:convert库:
| 1
2
3
4
5
6
7
 | import 'dart:convert';
... //省略中间代码 
String responseBody = await response.transform(Utf8Decoder()).join();
print(respinseBody)
 | 
最后关闭Client
Dio
Dio是Dart社区上别人上传的第三方库,他封装了HttpClient,相较来说更为简单、方便。
引入
在项目的pubspec.yaml文件的dependencies中导入Dio:
| 1
2
3
 | dependencies:
  dio: 
  // 如果冒号后面不带具体版本信息则表示自动下载最新版
 | 
接着在需要使用的代码文件里面,import导入他:
| 1
 | import 'package:dio/dio.dart';
 | 
接着我们就可以使用Dio了。
示例
首先得创建一个Dio对象。
发起GET请求:
| 1
2
3
 | Response response;
response=await dio.get(uri)
print(response.data.toString());
 | 
发起POST请求:
| 1
 | response=await dio.post(uri);
 | 
Json转Dart
手动生成Dart实体类
首先得大家安利一个网站,因为Dart实体类比Java实体类多了几个方法,所以相对来说麻烦,通过这个网站就可以自动生成json数据对应的实体类:
https://caijinglong.github.io/json2dart/

比方说我们使用这个链接(http://guolin.tech/api/china),并让他自动转dart:

接下来只需要创建dart文件,然后再把代码复制进去就行了。
不出意外,代码肯定会报错。

报错信息为:
| 1
2
3
4
5
 | 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文件中导入三个依赖包:
 我们重点只需要管三个写着需要导入的。输入之后点击
我们重点只需要管三个写着需要导入的。输入之后点击pubspec.yaml文件右上角的packages get,就会自动下载包了。
然后在终端中,转到项目根目录下,输入
| 1
 | flutter packages pub run build_runner build
 | 
接着你就会惊喜的发现,在你的dart实体类下面多了一个文件,也就是你所缺失的~.g.dart文件,并且实体类中那些报错也都没了。

将请求回来的Response转为Dart实体类
接着HttpClient返回来的responseBody或者Dio返回的response
| 1
2
 | List<Province> provinceList;
provinceList = ProvinceList.getProvinceList(jsonResponse);
 | 
provinceList就是返回来的数据转成的实体类的对象了。
异步更新UI
在此处介绍两种方法,第一种是我自己想出来的沙雕方法,第二种是通过Flutter提供的专门用于异步更新UI的组件FutureBuilder。
沙雕方法
在此说一下我这个方法的想法。
大家可以回顾下我之前讲Widget的时候说过,Widget有一个方法initState可以用来加载UI,以及一个setState方法可用来提醒Flutter重新加载UI。
说到这很多同学一定想到了,我们可以在获取到数据之后通过setState方法来更新UI。
获取到数据
这个不同多说,上面讲的全都是获取数据。
设置一个空的实体类
此处我还是拿上面的Province.dart来做例子。我们正常情况下获取到的数据转成的实体类的对象是List<Province> provinceList。所以我们此处先设置一个空的对象:List<Province> _provinceList。
接着在initState里面调用网络请求的方法:
| 1
2
3
4
5
 | @override
void initState() {
    getProvince();
    super.initState();
}
 | 
然后在getProvince方法里面获取数据,并将数据provinceList传给_provinceList:
| 1
2
3
4
5
6
7
 | void getProvince() async {
    var response = await Dio().get("http://guolin.tech/api/china");
    provinceList = ProvinceList.getProvinceList(response);
    setState(() {
        _provinces = provinceList;
    });
}
 | 
这样就可以当获取到数据的时候就通知Flutter更新状态了。这个时候有的同学可能会问我,你这个也只是获取到数据的时候才更新啊,那当还没获取到数据的时候呢?
这块就体现到了为什么我们要设置一个_provinces了。
我们完全可以在build方法设置下,判断下_provinces为不为空:如果为空,就证明没数据,就加载其它页面;如果不为空,就证明有数据了,那就加载数据。
| 1
2
3
4
5
 | body: _provinces == null
          ? new Text("正在请求")
          : new ListView.builder(
          ... //省去代码,总的来说就是将_provinces的数据加载ListView里面,一个ListView的基本构造方法
          )
 | 
FutureBuilder
回归正题,我上面那个方法真的是有够沙雕的。
那我们来看看这个名正言顺的Flutter亲生儿子FutureBuilder。
首先我们看下定义:
| 1
2
3
4
5
 | FutureBuilder({
  this.future,
  this.initialData,
  @required this.builder,
})
 | 
- future:异步任务;
- initialData:初始化数据;
- builder:builder方法。
示例
|  1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 | 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();
    },
)
 |