Flutter(十九)实战-网络请求与模型转换

1,537 阅读4分钟

「这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战

在项目开发过程中,发送网络请求获取数据是避免不了的需求,今天我们来讲解一下Flutter中的网络请求如何发送;

网络请求

Flutter中,发送网络请求一般会用到两个库:

  • http(官方的)

image.png

image.png

今天我们先使用官方的http发送网络请求;

配置网络请求库

首先进入http的地址:pub.dev/packages/ht…,网页内容如下:

image.png

点击图片中红框所示的按钮进行复制操作:

image.png

可以看到,我们拷贝了一串字符串:

http: ^0.13.4

那么,这个字符串有什么用呢?打开工程的pubspec.yaml文件,找到红框所示区域:

image.png

注意不要找错区域;

我们发现,在红框区域中,cupertina_icons: ^1.0.2跟我们刚才复制的http: ^0.13.4格式一模一样了;在此处配置的就是我们需要用到的一些库,我们将http的配置信息添加进去,该操作类似iOSCocoaPods的用法:

image.png

然后点击图中箭头所示的Pub get按钮,控制台输入如下信息即为载入成功,我们就可以使用http库了:

image.png

网络请求库的使用

我们已经将http载入成功,那么如何使用呢?首先,我们需要在使用http的页面,引入http,引入代码如下:

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

然后我们定义一个方法来请求数据,并且在initState中调用该方法:

@override
void initState() {
  super.initState();
  // 发送网络请求
  getConversationList();
}

getConversationList() {

}

但是需要注意的是,我们在getConversationList方法中发送网络请求,那么很明显,该方法应该是异步执行的,不能影响其他代码的执行,所以我们需要给getConversationList方法添加异步的标识:

getConversationList() async {

}

http发送请求

接下来就是放松网络请求了,http发送网络请求方式如下:

image.png

需要注意的是,get()是放松了一个Get方式的网络请求,其方法定义如下:

Future<Response> get(Uri url, {Map<String, String>? headers}) =>
    _withClient((client) => client.get(url, headers: headers));

可以看到,该方法是有返回值的;我们在开发iOS项目中,发送网络请求,请求的结果数据一般都是通过block回调返回;但是在Flutterhttp网络请求中,数据方法的直接返回值,而get()很明显应该是异步请求,所以我们需要使用await标记一下:

image.png

我们打印一下看看网络请求的结果:

image.png

解析数据

我们已经从服务器获取到了数据,接下来就是解析数据:

image.png

List<Chat> chatList = responseBody['chat_list'].map<Chat>((item) => Chat.formMap(item)).toList();

这句代码的意思是:从responseBody中取出chat_list这个数组,然后map方法将会循环遍历此数组,遍历的元素为item,将item通过Chat.fromMap方法转换成Chat模型数据,最终toList()意为将所有的转换之后的Chat放进数组中,返回一个List; ​

我们来看一下打印结果:

image.png

异步方法返回值

在以往iOS的开发中,网络请求的数据,经过方法的处理之后,我们通常通过block的形式跑出来,那么在Flutter中,异步的方法能够接收返回值呢?答案当然是可以的,我们需要将方法作如下修改:

image.png

Future来接收返回值;表示这是一个未来的数据;

接下来看一下,getConversationList方法的返回值是如何处理的:

image.png

也可以使用链式调用:

getConversationList().then((value) {
	print('===$value');
});

我们看一下打印结果:

image.png

网络请求的处理

我们已经知道在then中能够获取到网络请求的数据,那么是否还有其他状态的处理呢?

  • 错误处理

catchError用来捕获错误信息;

image.png

  • 请求完成

whenComplete在网络请求完成时回调;

image.png

  • 设置超时时间

timeout可以设置网络请求的超时时间; image.png

我们将超时时间设置为1毫秒,看一下打印信息:

image.png

我们发现,即使网络请求超时了,但是thenwhenComplete的回调依然执行了,这是因为在Flutter中即使只要网络请求发出,那么就一定会有结果,而结果的状态需要我们自己去控制,比如通过自定义的bool值:

dio发送请求

dio是国内的大神开发的请求框架,我们后续再细讲; 想研究的,可以前往dio使用

模型转换

我们的网络请求返回的数据一般是json格式的,那么我们如果将json字符串转换成模型数据呢?

Map转json

引入头文件:

import 'dart:convert';

引入头文件之后,可直接使用json调用encode方法转换; ​

json转map方式如下:

final map = {'name': '张三','age': '30','nickName': '法外狂徒',};
final jsonStr = json.encode(map);
print('map: $map');
print('json: $jsonStr');
  • json:调用者,引入头文件之后可直接使用;
  • mapMap类型数据;
  • jsonStr:转换之后的json字符串;

打印结果:

flutter: map: {name: 张三, age: 30, nickName: 法外狂徒}
flutter: json: {"name":"张三","age":"30","nickName":"法外狂徒"}

json转Map

final newMap = json.decode(jsonStr);
print('new: $newMap');
  • json:调用者,引入头文件之后可直接使用
  • jsonStr:需要转换的json字符串;
  • newMap:转换之后的Map类型数据;

打印结果:

flutter: new: {name: 张三, age: 30, nickName: 法外狂徒}

json转模型

我们先定义一个Chat的模型数据,格式如下:

class Chat {
  final String? name;
  final String? message;
  final String? avatar;
  Chat({this.name, this.message, this.avatar});

  factory Chat.formMap(Map map) {
    return Chat(
      name: map['name'],
      message: map['message'],
      avatar: map['avatar']
    );
  }
}
  • factory:工厂构造,可以定义构造方法的返回值;

转换方式如下:

final map = {'name': '张三','message': '你好','avatar': 'https://randomuser.me/api/portraits/men/21.jpg',};
final chat = Chat.formMap(map);
print('name:${chat.name}; avatar:${chat.avatar}; message:${chat.message}');

打印结果:

flutter: name:张三; avatar:https://randomuser.me/api/portraits/men/21.jpg; message:你好