颤动|定义各种网络请求 Widget
说到监控网络请求,我们是不是首先想到FutureBuilder
?
FutureBuilder
为我们涵盖了网络请求的各种状态。
此帖仅供参考。积极地一起讨论,你也会得到一些建议!
这就是结果。
首先服务没有运行:
可以看到所有的错误信息,
然后启用服务:
1。首先定义一个通用的网络请求
既然是网络请求,那么我们首先需要定义一个通用的网络请求方法。
每个后端API都有不同的方法,有的是RSETful,有的是我们比较熟悉的GET和POST。
这里以GET为例,API服务为GitHub - 网易云音乐Node.js API服务。
网络请求使用Dio。首先创建NetUtils.dart
。
启动代码:
static Dio _dio;
static void init() async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
CookieJar cj = PersistCookieJar(dir: tempPath);
_dio = Dio(BaseOptions(baseUrl: 'http://127.0.0.1:3000'))
..interceptors.add(CookieManager(cj))
..interceptors.add(LogInterceptor(responseBody: true, requestBody: true));
}
复制代码
在runApp之前调用,完成初始化。
然后定义一个通用的网络请求:
static Future<Response> _get(
BuildContext context,
String url, {
Map<String, dynamic> params,
}) async {
Loading.showLoading(context);
try {
return await _dio.get(url, queryParameters: params);
} on DioError catch (e) {
if (e.response is Map) {
return Future.value(e.response);
} else {
return Future.error(0);
}
} finally {
Loading.hideLoading(context);
}
}
复制代码
这里的代码很简单。该方法需要传递三个参数:
- context:用于showLoading
- url:API地址
- params:网络请求 方法中参数
可以为空,我们得到❀ ,然后判断接口是否返回自定义内容。
示例:如果状态码不是2xx,但仍在返回数据,Dio将抛出DioError
,我们应该捕获并处理它。
如果恢复正常数据,我们仍然会恢复。如果不是正常数据,直接抛出Future.error(0)
。
使用这个通用方法:
/// 新碟上架
static Future<AlbumData> getAlbumData(
BuildContext context, {
Map<String, dynamic> params = const {
'offset': 0,
'limit': 10,
},
}) async {
var response = await _get(context, '/top/album', params: params);
return AlbumData.fromJson(response.data);
}
复制代码
我们可以使用这样定义的方法,它简化了以下FutureBuilder
的定义。
2。确认网络请求控制器需要的功能
在第一张图中我们可以清楚地看到,实际上有三个功能:
- 请求数据并显示Loading
- 有正常数据时恢复正常,当有是错误,返回Widget错误
- 如果错误是Widget,可以再次按请求
不过细心的同学也发现了问题。
我们在网络请求中添加了Loading,需要一个BuildContext。
我们都知道这个BuildContext不能在initState()
方法中使用。
因此,我们必须做第一次会议:
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((call) {
_request();
});
}
复制代码
完成对我们的应用程序的研究。
3。写全局网络请求控制
讨论全局网络请求控制,实际上是FutureBuilder
的封装层。
请求数据并显示Loading
但是,这里也存在一个问题:
当我们最初定义网络请求工具类时,每个网络请求都是一个方法,而Each方法有或没有参数。
我们也知道FutureBuilder
未来需要通过,那我们该怎么办呢?
而且我们在使用该控件时无法调用网络请求方法,因为网络请求中包含Loading,而那个Loading需要BuildContext
。
这种情况我们只能传递给(Function)方法:
typedef ValueWidgetBuilder<T> = Widget Function(
BuildContext context,
T value,
);
final ValueWidgetBuilder<T> builder;
final Function futureFunc;
final Map<String, dynamic> params;
CustomFutureBuilder({
@required this.futureFunc,
@required this.builder,
this.params,
});
复制代码
这样我们就可以在第一帧回调中调用网络请求,一石二鸟。 :
使用该控件时无需调用方法,也避免了Loading时使用BuildContext的报错问题。
Future<T> _future;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((call) {
_request();
});
}
void _request() {
setState(() {
if (widget.params == null)
_future = widget.futureFunc(context);
else
_future = widget.futureFunc(context, params: widget.params);
});
}
复制代码
首先我们定义未来,然后我们以第一次再次通话开始未来。
正常时返回正常数据,返回Widget错误
这就需要我们将封装的网络请求与FutureBuilder
、根据网络逻辑进行连接:这可以对应多个FutureBuilder
的状态:
- 网络请求->
ConnectionState.none,
- 显示正在加载->
ConnectionState。活动
- 请求结束 ->
ConnectionState.done
- 是否有数据(true or false)->
snapshot.hasData
- 然后抛出错误->snapshot
明白了这一点,我们就可以编写代码了:
Widget build(BuildContext context) {
return _future == null
? Container(
alignment: Alignment.center,
height: ScreenUtil().setWidth(200),
child: CupertinoActivityIndicator(),
)
: FutureBuilder(
future: _future,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
return Container(
alignment: Alignment.center,
height: ScreenUtil().setWidth(200),
child: CupertinoActivityIndicator(),
);
case ConnectionState.done:
if (snapshot.hasData) {
return widget.builder(context, snapshot.data);
} else if (snapshot.hasError) {
return NetErrorWidget(
callback: () {
_request();
},
);
}
}
return Container();
},
);
}
复制代码
首先判断_future是否不存在。如果为空,则表示future未初始化。
此时我建议重新设置自定义的Loading Widget,因为这个Widget会在下次网络请求时恢复,以免混淆。
然后查看ConnectionState.done
中是否有数据。如果是这样,请展示未来的Widget。
如果返回错误,则返回错误。
Widget Error 可以再次点击请求
这个逻辑很简单,我之前提到的文章中有部分解释。
FutureBuilder什么时候回来?
@override
void didUpdateWidget(FutureBuilder<T> oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.future != widget.future) {
if (_activeCallbackIdentity != null) {
_unsubscribe();
_snapshot = _snapshot.inState(ConnectionState.none);
}
_subscribe();
}
}
复制代码
您可以看到,如果两个 Future 不相同,则会重复该过程。如果没有,我不会离开。
而我们在上面定义了它,由于启用了Function和Params,我们可以随时重新创建Future:
void _request() {
setState(() {
if (widget.params == null)
_future = widget.futureFunc(context);
else
_future = widget.futureFunc(context, params: widget.params);
});
}
复制代码
Widget的误点击事件是这样写的,It will come。 FutureBuilder
,即请求。
作者:Flutter Notes
链接:https://juejin.im/post/5d9b4cd3f265da5bb746495f
来源:掘金
商业印刷请联系作者获得许可。非商业转载请注明来源。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。