异步函数
对于一个异步函数来说,其返回时内部执行动作并未结束,因此需要返回一个 Future 对象,供调用者使用。调用者根据 Future 对象,来决定:
void syncFunDemo() async{
Future future() => Future.delayed(Duration(seconds: 3),()=>“hello 2021”);
// future.then((value) => print(“获取到异步数据$value”));//等待完成后,执行then里面的函数
print(“获取到异步数据”+ (await future()));//同步等待
}
为什么要加上 async?
因为 Dart 中的 await 并不是阻塞等待,而是异步等待。Dart 会将调用体的函数也视作异步函数,将等待语句的上下文放入 Event Queue 中,一旦有了结果,Event Loop 就会把它从 Event Queue 中取出,等待代码继续执行。
在了解了异步操作之后,趁热打铁,学习flutter 中的网络请求。
网络与服务端数据交互时,不可避免地需要用到三个概念:定位、传输与应用。
其中,定位,定义了如何准确地找到网络上的一台或者多台主机(即 IP 地址);传输,则主要负责在找到主机后如何高效且可靠地进行数据通信(即 TCP、UDP 协议);而应用,则负责识别双方通信的内容(即 HTTP 协议)。
一般的网络请求框架中,依次http 网络调用 可以分为以下几个部分:
在 Flutter 中,Http 网络编程的实现方式主要分为三种:dart:io 里的 HttpClient 实现、Dart 原生 http 请求库实现、第三方库 dio 实现。
HttpClient 和 http 使用方式虽然简单,但其暴露的定制化能力都相对较弱,很多常用的功能都不支持(或者实现异常繁琐),比如取消请求、定制拦截器、Cookie 管理等。因此对于复杂的网络请求行为,我推荐使用目前在 Dart 社区人气较高的第三方 dio 来发起网络请求。
dio: 3.0.10
void getRequest() async {
//创建网络调用示例
Dio dio = Dio();
//设置URI及请求user-agent后发起请求
var response = await dio.get(“https://wanandroid.com/wxarticle/chapters/json”,
options: Options(headers: {“user-agent”: “Custom-UA”}));
//打印请求结果
if (response.statusCode == HttpStatus.ok) {
print(response.data.toString());
} else {
print(“Error: ${response.statusCode}”);
}
}
// 同时发起多个请求
void getRequest2() async {
//创建网络调用示例
Dio dio = Dio();
//同时发起两个并行请求
List responseX = await Future.wait([
dio.get(“https://wanandroid.com/wxarticle/chapters/json”),
dio.get(“https://www.wanandroid.com/article/list/0/json”)
]);
//打印请求1响应结果
print(“Response1: ${responseX[0].toString()}”);
//打印请求2响应结果
print(“Response2: ${responseX[1].toString()}”);
}
// 添加拦截器
void getRequest3() async {
//创建网络调用示例
Dio dio = Dio();
//增加拦截器
dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options) {
//为每个请求头都增加user-agent
options.headers[“user-agent”] = “Custom-UA”;
print(“interceptor request ${options.uri}”);
print(“interceptor request ${options.headers}”);
print(“interceptor request ${options.data}”);
//放行请求
return options;
}, onResponse: (Response response) {
print(“interceptor response ${response.data.toString()}”);
}));
//增加try catch,防止请求报错
try {
await dio.get(“https://wanandroid.com/wxarticle/chapters/json”);
} catch (e) {
print(e);
}
}
以上都是dio 简单的网络请求示例,更多的高级用法,可以到 GitHub 去看看。
在完成了网络请求后,得到的数据我们还不能直接使用,要先解析服务器给我们返回的数据。而json 是常用的服务端和客户端传输一种数据格式。
在拿到服务端返回的json 数据后,如何解析呢?
由于 Flutter 不支持运行时反射,因此并没有提供像 Gson、Mantle 这样自动解析 JSON 的库来降低解析成本。在 Flutter 中,JSON 解析完全是手动的,开发者要做的事情多了一些,但使用起来倒也相对灵活。
自动解析:使用 dart:convert 库中内置的 JSON 解码器,将 JSON 字符串解析成自定义对象的过程。使用这种方式,我们需要先将 JSON 字符串传递给 JSON.decode 方法解析成一个 Map,然后把这个 Map 传给自定义的类,进行相关属性的赋值。
class Student{
//属性id,名字与成绩
String id;
String name;
int score;
//构造方法
Student({
this.id,
this.name,
this.score
});
//JSON解析工厂类,把map 解析成model 对象
factory Student.fromJson(Map
return Student(
id: parsedJson[‘id’],
name : parsedJson[‘name’],
score : parsedJson [‘score’]
);
}
}
void parseJson1(){
var jsonString = “”"{ “id”:“1234”, “name”:“周结”, “score” : 95}""" ;
//jsonString为JSON文本
final jsonResponse = json.decode(jsonString);
Student student = Student.fromJson(jsonResponse);
print(student.name);
}
json 数据解析生成类中,都有一个 json 解析工厂;要是里面嵌套 比较多层,估计解析起来也挺累的,这样的事情交给插件就可以了。 在 Android studio plugins 中搜索 FlutterJsonBeanFactory
安装该插件,然后重启studio。
/// 使用 FlutterJsonBeanFactory插件实现 json转model
void
parseJson2() {
var jsonString = “”"{ “id”:“123”, “name”:“张三”, “score” : 95}""";
//jsonString为JSON文本
final jsonResponse = json.decode(jsonString);
UserEntity student = JsonConvert.fromJsonAsT(jsonResponse);
print(student.name);
}
FlutterJsonBeanFactory 插件帮我们做了好多事情,让我们更能专注于开发。
通过上面的学习,我们了解了在dart 中的网络请求和json 解析,完成了从远程获取数据的学习,接下来看看 flutter 如何处理本地数据的。
Flutter 提供了三种数据持久化方法,即文件、SharedPreferences 与数据库。
文件是存储在某种介质(比如磁盘)上指定路径的、具有文件名的一组有序信息的集合。从其定义看,要想以文件的方式实现数据持久化,我们首先需要确定一件事儿:数据放在哪儿?这,就意味着要定义文件的存储路径。
Flutter 提供了两种文件存储的目录,即临时(Temporary)目录与文档(Documents)目录:
// 文件的读取,要先依赖 path_provider: ^2.0.1
class LocalDataDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(“本地数据读取”),
),
body: ListView(
children: [
ListTile(
title: Text(“本地文件读写”),
onTap: () => Navigator.pushNamed(context, “LocalFileDemo”),
),ListTile(
title: Text(“SharePreferenceDemoDemo”),
onTap: () => Navigator.pushNamed(context, “SharePreferenceDemo”),
)
],
),
);
}
}
class LocalFileDemo extends StatelessWidget {
//创建文件目录
ed(context, “LocalFileDemo”),
),ListTile(
title: Text(“SharePreferenceDemoDemo”),
onTap: () => Navigator.pushNamed(context, “SharePreferenceDemo”),
)
],
),
);
}
}
class LocalFileDemo extends StatelessWidget {
//创建文件目录