logo头像

aferica

Flutter学习(五):网络请求封装优化

本文于1642天之前发表,文中内容可能已经过时。

在之前的文章Flutter学习(三):网络请求Dio模块使用中,我简单的介绍了Dio模块的使用和封装,比较浅显,使用时有很多不足和重复,在本文中,我将对封装进行进一步的优化。

常用功能配置

设置网络请求的拦截器在日常开发中使用非常广泛,例如:添加统一认证Header,判断登录状态已跳转登录页
简单实现:

1.创建Dio对象

推荐在一个工具文件中定义和初始化Dio对象,同时还可以定制Get、Post

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* utils/request.dart
* 初始化和基本封装
*/
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';

// 自定义的网络请求加载弹框组件
import 'components/Dialog.dart';

// 设置统一请求连接时间和超时时间
BaseOptions options = new BaseOptions(
connectTimeout: 5000,
receiveTimeout: 10000,
responseType: ResponseType.plain
);
Dio dio = new Dio(options);

class Request {
// ... 自定义封装,详情查看第3步
}

2.添加设置全局拦截器

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* main.dart
* 项目入口文件
*/
import 'package:flutter/material.dart';
import 'package:fluro/fluro.dart';
import 'package:dio/dio.dart';
import 'package:fluttertoast/fluttertoast.dart';

import 'utils/request.dart';
import 'utils/route.dart';

void main() {
// 添加路由
final router = new Router();
Routes.configureRoutes(router);
Routes.router = router;
// 添加dio拦截器
dio.interceptors.add(InterceptorsWrapper(
// 在请求被发送之前做一些事情
onRequest: (RequestOptions options) async {
print('请求地址:' + options.uri.toString());
/**
* 添加统一认证
*/
const token = ''; // 这里token保存和获取可以使用SharedPreferences模块
if(token == '') {
// 处理未登录情况,如跳转到登录页
Fluttertoast.showToast(msg: '请登录后使用', backgroundColor: Colors.black54, fontSize: 14.0);
// 结束请求 errMsg可以为任何类型,例如字符串、数字、Map等
return dio.reject(errMsg);
} else {
// 使用jwt认证
options.headers['Authorization'] = 'Bearer ' + token;
// 或者添加token
options.headers['token'] = token;
// continue
return options;
}
onResponse: (Response response) async {
print(response.data.toString());
// 对返回数据JSON数据处理
// 例如`[{"":""},{"":""},{"":""}]`
// 需要使用`{}`处理后才可以转为Map
String tempRes = response.data.toString();
if(tempRes[0] == '[') {
tempRes = '{"reslut":' + tempRes + '}';
}
Map<String, dynamic> result = json.decode(tempRes.toString());
response.data = result;
return response;
},
onError: (DioError e) {
if(e.type == DioErrorType.CONNECT_TIMEOUT || e.type == DioErrorType.RECEIVE_TIMEOUT || e.type == DioErrorType.SEND_TIMEOUT) {
Fluttertoast.showToast(msg: '网络请求超时,请检查网络后重试', backgroundColor: Colors.black54, fontSize: 12.0);
}
if(e.type == DioErrorType.CANCEL) {
Fluttertoast.showToast(msg: '用户取消请求', backgroundColor: Colors.black54, fontSize: 12.0);
}
return e;
}
));
runApp(MyApp());
}

class MyApp extends StatelessWidget {
// 省略
}

3.请求方法封装

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
/**
* utils/request.dart
* 初始化和基本封装
*/
... // 续接第1步
class Request {
// 自定义封装 已get方法为例
///
/// @require url 请求地址
/// @require context 请求页BuildContext对象
/// showLoading 是否显示加载提示框
/// closeLoading 是否关闭加载提示框
/// 这两个通常配合使用
/// 当连续请求,如首页加载过多信息时,可以通过设置第一次显示提示框,不关闭
/// 最后一次只关闭,不显示,中间不显示不关闭来避免多次闪烁的问题
///
static Future<Map<String, dynamic>> get(String url, BuildContext context,{ bool showLoading = true, bool closeLoading = true}) async {
if (showLoading) {
showDialog<Null>(
context: context,
builder: (_) => LoadingDialog()
);
}
Response res = await dio.get(url);

if (closeLoading) {
Navigator.pop(context);
}
if (res.statusCode != 200) {
return {};
}
return res.data;
}
}

4.使用示例

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
// 引入工具包
import 'utils/request.dart';

// 在initState方法中调用

...

class xxxState extends State<xxx> {
...
@override
void initState() {
// TODO: implement initState
super.initState();
// 解决inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before VideoInfoState.initState() completed报错
WidgetsBinding.instance.addPostFrameCallback((_){
getInfo();
});
}

getInfo () {
String url = 'xxx';
Map<String, dynamic> infoMap = await Request.get(url, context);
}
...
}

...

使用效果

注意:inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before VideoInfoState.initState() completed报错

在使用时,如果出现inheritFromWidgetOfExactType(_LocalizationsScope) or inheritFromElement() was called before VideoInfoState.initState() completed报错,
如下图:
20190710142813.png
查看自己initState中调用网络请求方法是否使用WidgetsBinding.instance.addPostFrameCallback((_){}处理

微信打赏

你的赞赏是对我最大的鼓励