基於FutureBuilder通用網路請求介面封裝
在Dart中,處理非同步有兩種方式,一種方式是asyn/awiat ,另一種方式就是FutureBuilder,FutureBuilder能夠在網路請求開始前,請求中,請求完成or失敗,幫助我們管理我們的UI介面。
FlutterBuilder原始碼
///構造方法以及對應引數型別
const FutureBuilder({
Key key,
this.future,
this.initialData,
@required this.builder,
}) : assert(builder != null), super(key: key);
final Future<T> future; //獲取資料非同步操作
final AsyncWidgetBuilder<T> builder;//根據狀態返回不同widget
///ConnectionState四種狀態
enum ConnectionState {
/// Not currently connected to any asynchronous computation.
/// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.
none,
/// Connected to an asynchronous computation and awaiting interaction.
waiting,
/// Connected to an active asynchronous computation.
/// For example, a [Stream] that has returned at least one value, but is not
/// yet done.
active,
/// Connected to a terminated asynchronous computation.
done,
}
複製程式碼
FutureBuilder簡單使用Demo
FutureBuilder(
future: fetchPost(),//網路請求操作
builder:
(BuildContext context, AsyncSnapshot<CommonModel> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text("Input a url to connect");
case ConnectionState.waiting:
//圓形載入widget
return Center(child: CircularProgressIndicator());
case ConnectionState.active:
return Text("");
case ConnectionState.done:
if (snapshot.hasError) {
return Text(
"${snapshot.error}",
style: TextStyle(color: Colors.red),
);
} else {
//正常資料返回回撥
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
"網路資料返回",
style: TextStyle(fontSize: 20),
),
Text("title:${snapshot.data.title}"),
Text("icon:${snapshot.data.icon}"),
Text("url:${snapshot.data.url}"),
],
);
}
}
})
複製程式碼
FutureBuilder簡單封裝
接下來開始我們的通用網路Widget封裝,根據Demo我們發現需要一個可以返回Future網路請求方法,載入介面Widget,載入出錯的Widgwt以及網路請求結束正常展示資料Widget。
框架思考需要實現功能:
- 請求網路的方法 loadData
- 資料正常返回,給Widget介面賦值;
- 載入error Widget,網路錯誤or無網狀態下,點選錯誤Widget重新觸發網路請求;
- 提供預設的loading 以及 error Widget,同時使用者也可自定義想要的提示介面;
開始實現功能
1.定義傳遞請求方法
///FutureBuilder的future傳的內容
typedef LoadDataFuture<T> = Future<T> Function(BuildContext context);
複製程式碼
2.定義抽象類(資料正常展示Widget)
abstract class NetNormalWidget<T> extends StatelessWidget {
final T bean;//通用資料類
NetNormalWidget({this.bean});
@override
Widget build(BuildContext context) {
return buildContainer(bean);
}
///給定義Widget賦值
Widget buildContainer(T t);
}
複製程式碼
3.定義網路出錯Widget以及對應點選回撥
///net出錯 回撥
abstract class ErrorCallback {
void retryCall();
}
///網路請求 失敗 Widget
class NetErrorWidget extends StatelessWidget {
final Widget errorChild;
final ErrorCallback callback;
NetErrorWidget({@required this.errorChild, this.callback});
@override
Widget build(BuildContext context) {
return GestureDetector(
child: errorChild,
onTap: () => callback?.retryCall(),
);
}
}
複製程式碼
4.開始拼裝框架
///定義該框架需要屬性實現對應State
class FutureBuilderWidget<T> extends StatefulWidget {
final Widget loadingWidget;
final Widget errorWidget;
final NetNormalWidget<T> commonWidget;
final LoadDataFuture<T> loadData;
FutureBuilderWidget(
{@required this.commonWidget,
@required this.loadData,
this.loadingWidget,
this.errorWidget});
@override
State<StatefulWidget> createState() => _FutureBuilderWidgetState<T>();
}
///實現State方法並mixins網路出錯點選回撥
class _FutureBuilderWidgetState<T> extends State<FutureBuilderWidget<T>>
with ErrorCallback {
@override
void initState() {
super.initState();
widget.loadData(context);
}
///預設載入介面
final defaultLoading = Center(
child: CircularProgressIndicator(),
);
///預設出錯介面
Widget _defaultError(dynamic error) {
return Center(
child: Text(error.toString()),
);
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: widget.loadData(context),
builder: (BuildContext context, AsyncSnapshot<T> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
break;
case ConnectionState.waiting:
case ConnectionState.active:
return widget.loadingWidget ?? defaultLoading;
case ConnectionState.done:
if (snapshot.hasError) {
///網路出錯
if (widget.errorWidget != null) {
///自定義出錯介面
if (widget.errorWidget is NetErrorWidget) {
return widget.errorWidget;
} else {
///只自定義介面顯示內容
return NetErrorWidget(
errorChild: widget.errorWidget,
callback: this,
);
}
} else {
///選用預設出錯介面
return NetErrorWidget(
errorChild: _defaultError(snapshot.error),
callback: this,
);
}
}
return widget.commonWidget.buildContainer(snapshot.data);
}
});
}
@override
void retryCall() {
widget.loadData(context);
setState(() {
///通知State重新構建介面需要
});
}
}
複製程式碼
通用框架呼叫
class FutureBuilderDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _FutureBuilderState();
}
class _FutureBuilderState extends State<FutureBuilderDemo> with ErrorCallback{
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FutureBuilder控制元件封裝"),
),
body: FutureBuilderWidget<CommonModel>(
commonWidget: CommonWidget(),
loadData: _loadData,
//自定義出錯介面以及對應店家回撥事件(本例子直接關閉介面)
// errorWidget: NetErrorWidget(
// callback:this,
// errorChild: Center(
// child: Text("網路出錯 點選返回"),
// ),
// ),
),
);
}
///網路請求庫
Future<CommonModel> _loadData(BuildContext context) async {
Api.baseUrl = 'http://www.devio.org/io/flutter_app/';
final resp = await HttpUtil.instance.fetchGet('json/test_common_model.json');
return CommonModel.fromJson(resp);
}
@override
void retryCall() {
Navigator.of(context).pop();
}
}
///實現抽象方法,直接給介面複製
class CommonWidget extends NetNormalWidget<CommonModel> {
@override
Widget buildContainer(CommonModel t) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(t.title),
Text(t.icon),
],
),
);
}
}
複製程式碼