接上文,这篇文章主要介绍Flutter网络请求, 页面不同状态展示的封装
网络请求
主要使用的是dio库进行网络请求,引入的第三个库。对于Flutter的自带的HttpClient也可以。用习惯了dio还是放不下。或者有兴趣的可以自己对于HttpClient封装一下
引入dio
在pubspec.yaml文件中加入
dio: ^3.0.10
引入头文件方式
import 'package:dio/dio.dart';
简单封装使用dio
先分析一下使用场景,比较常见的场景是用户登录之后获取到token之后,设置在请求头中进行请求获取数据。接下来沿着这个思路设计一下。 新建一个base.dart文件
import 'package:dio/dio.dart';
Dio initDio() {
BaseOptions _baseOptions = BaseOptions(
baseUrl: '设置服务器地址', connectTimeout: 15000);
Dio dio = Dio(_baseOptions);
dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
print('走网络请求了设置头部了');
options.queryParameters['Authorization'] =
SpUtil.getString('token', defValue: '');
return options;
}));
return dio;
}
class Api {
static Dio dio = initDio();
static Future<Map> requestApi(method, path, data) async {
Response result;
try {
if (method == 'get') {
result = await dio.get(path, queryParameters: data);
} else if (method == 'post') {
result = await dio.post(path, data: data);
}
print(result);
return result.data;
} on DioError catch (e) {
print(e);
if (e == null) {
return Future.error(Response(data: -1));
} else if (e.response != null) {
if (e.response.statusCode >= 300 && e.response.statusCode < 400) {
return Future.error(Response(data: -1));
} else {
result = e.response;
return Future.value(result.data);
}
} else {
return Future.error(Response(data: -1));
}
} finally {
print('网络请求结束');
}
}
}
以上初始化了dio。具体的请求我们可以再创建文件。放在一起比较乱。比如首页的请求放在一起,我的页面的请求放在一起。应该能理解哈
class ReqHome {
Future<Map> loginInApi(username, password) {
return Api.requestApi('post', '/login',
{"username": username, "password": password});
}
static Future<Map> getSwiperApi() {
return Api.requestApi('get', '地址', {});
}
}
下面就是调用了,已登录为例
Map result = await ReqHome().loginInApi('用户名', '密码');
// 这样在获取到token之后在存入到本地
SpUtil.putString('token', '获取的token');
获取到token之后需要把token设置在header中
dio.interceptors
.add(InterceptorsWrapper(onRequest: (RequestOptions options) async {
// 在这添加!!!!!!
options.queryParameters['Authorization'] =
SpUtil.getString('token', defValue: '');
return options;
}));
页面不同状态展示封装
我是用FutureBuilder来控制页面显示不同状态。首先创建个类common_widget文件, 线分析一波,页面状态分成加载中, 加载完成空数据, 加载完成展示UI,加载失败。 演示一下加载成功的图视: 首先定义三个属性
// final Future networkApi; // 传进来的网络请求
// final Function onSuccess; // 加载成功之后把数据传递出去
// final Widget successWidget; // 加载成功的视图
下面是主要代码:
@override
Widget build(BuildContext context) {
return FutureBuilder<Map>(
future:
widget.networkApi,
builder: (BuildContext context, AsyncSnapshot snapshot) {
// 请求已结束
if (snapshot.connectionState == ConnectionState.done) {
print(snapshot);
if (snapshot.hasError) {
// 请求失败,显示错误
return _errorView;
} else {
// 请求成功,显示数据
if (snapshot.data == null || snapshot.data.length == 0) {
return _emptyView;
}
widget.onSuccess();
return widget.successWidget;
}
} else {
// 请求未结束,显示loading
return Center(
child: CircularProgressIndicator(),
);
}
});
}
调用代码:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('首页'),
),
body: CommonWidget(
networkApi: ReqHome.getSwiperApi(),
successWidget: successWidget(),
onSuccess: () {},
));
}
展示错误UI
在common_widget文件中加入代码:
///错误视图
Widget get _errorView {
return Container(
width: double.infinity,
height: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// Image.asset(
// 'assets/images/load_error_view.png',
// height: 80,
// width: 100,
// ),
Text("加载失败,请重试"),
RaisedButton(
color: Colors.red,
onPressed: () {
setState(() {});
},
child: Text(
'重新加载',
style: TextStyle(color: Colors.white),
),
)
],
),
);
}
效果如下:
展示空数据UI
///数据为空的视图
Widget get _emptyView {
return Container(
width: double.infinity,
height: double.infinity,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 10),
child: Text('暂无数据'),
)
],
),
);
}
效果图:
视图可以根据UI设计去修改,答题结构就是这样子的。。
one more things。。。。。。
- 列表数据刷新加载封装
- 一些工具类封装