初步接触
刚开始接触Provider的时候,感觉比较懵逼,因为之前连RN的状态管理Redux都没搞明白,主要是觉得太繁琐了吧。在使用Provider之前,看过一些BLOC,ScopeModel等框架,但是感觉起来还是Provider更好用吧。Provider也是谷歌官方所推荐的。
学习Provider,网上看到最多的是Counter计数器。这里我们不讲计数器,我们谈一个项目中比较实际一点的的LoaderContainer (加载器)。
首先,LoaderContainer是我编写的一个加载器类
- LoaderContainer 加载器组件,声明了加载器状态值,加载器在不同状态下呈现的视图。
import 'package:flutter/material.dart';
import 'package:project_name/core/widget/rounded_button.dart';
import 'package:project_name/data/app/app_constants.dart';
enum LoaderState { NoAction, Loading, Succeed, Failed, NoData }
class LoaderContainer extends StatefulWidget {
LoaderContainer({
Key key,
@required this.contentView,
this.loadingView,
this.errorView,
this.emptyView,
@required this.loaderState,
this.onReload,
}) : super(key: key);
final LoaderState loaderState;
final Widget loadingView;
final Widget errorView;
final Widget emptyView;
final Widget contentView;
final Function onReload;
@override
State createState() => _LoaderContainerState();
}
class _LoaderContainerState extends State<LoaderContainer> {
@override
Widget build(BuildContext context) {
Widget currentWidget;
switch (widget.loaderState) {
case LoaderState.Loading:
currentWidget = widget.loadingView ?? _ClassicalLoadingView();
break;
case LoaderState.Failed:
currentWidget = widget.errorView ??
_ClassicalErrorView(
onReload: () => widget.onReload(),
);
break;
case LoaderState.NoData:
currentWidget = widget.emptyView ?? _ClassicalNoDataView();
break;
case LoaderState.Succeed:
case LoaderState.NoAction:
currentWidget = widget.contentView;
break;
}
return currentWidget;
}
}
class _ClassicalLoadingView extends StatelessWidget {
@override
Widget build(BuildContext context) => Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation(AppConstants.themeColor),
),
Padding(
padding: const EdgeInsets.only(top: 15),
child: Text(
'正在拼命加载中...',
style: TextStyle(
fontSize: 13,
color: Color(0xff999999),
),
),
),
],
),
);
}
class _ClassicalErrorView extends StatelessWidget {
_ClassicalErrorView({@required this.onReload}) : super();
final Function onReload;
@override
Widget build(BuildContext context) => Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'asset/img/ic_default_load_data_failed.png',
width: 80,
height: 80,
color: Color(0xff999999),
),
Padding(
padding: const EdgeInsets.only(top: 12),
child: Text(
'加载失败,请稍后点击重试',
style: TextStyle(
fontSize: 13,
color: Color(0xff999999),
),
),
),
Padding(
padding: const EdgeInsets.only(top: 20),
child: RoundedButton(
bgColor: AppConstants.themeColor,
onPressed: onReload,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 5, horizontal: 20),
child: Text(
'重新加载',
style: TextStyle(color: Theme.of(context).buttonColor),
),
),
),
),
],
),
);
}
class _ClassicalNoDataView extends StatelessWidget {
@override
Widget build(BuildContext context) => Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Image.asset(
'asset/img/ic_default_load_data_failed.png',
width: 80,
height: 80,
color: Color(0xff999999),
),
Padding(
padding: const EdgeInsets.only(top: 0),
child: Text(
'暂无相关数据 /(ㄒoㄒ)/~~',
style: TextStyle(
fontSize: 13,
color: Color(0xff999999),
),
),
),
],
),
);
}
- 准备一个加载器状态的Model,在Provider中,要求变化的值能处理相关变化,必须继承相关类,如:ChangeNotifier(常用)等。
import 'package:flutter/cupertino.dart';
import 'package:project_name/core/widget/loader_container.dart'
as LoaderStater;
/// 加载器的状态Model类
class LoaderStateModel extends ChangeNotifier {
LoaderStater.LoaderState _state = LoaderStater.LoaderState.Loading;
/// 构造函数
LoaderStateModel([this._state]) {
this._state = this._state ?? LoaderStater.LoaderState.Loading;
}
/// 修改状态
///
/// [state] LoaderState的取值
void changeState(LoaderStater.LoaderState state) {
_state = state;
notifyListeners();
}
/// 获取当前加载器的状态
LoaderStater.LoaderState get state => _state;
}
- 页面中处理加载器状态, 首页要给Page包装一个Provider,这里写在跳转这个页面的方法处。
return MultiProvider(providers: [
ChangeNotifierProvider<LoaderStateModel>.value(
value: LoaderStateModel(LoaderState.Succeed))
], child: PresaleOrderDetailInfoPage());
- 之前包装过Provider,那么在PresaleOrderDetailInfoPage中就能接收到Provider共享的状态值了。一般情况下,我们接收值使用Provider.of<xxx>(context)来接收,如果只是接收值可以考虑使用Consumer或其相关扩展函数。
class PresaleOrderDetailInfoPage extends StatefulWidget {
PresaleOrderDetailInfoPage({Key key}) : super(key: key);
@override
State<StatefulWidget> createState() => _PresaleOrderDetailInfoPageState();
}
class _PresaleOrderDetailInfoPageState
extends BasePageState<PresaleOrderDetailInfoPage>
with TickerProviderStateMixin {
/// 加载状体模型
LoaderStateModel _loaderStateModel;
/// 调用网络获取订单详情信息
void _getOrderDetailInfo() async {
_loaderStateModel.changeState(LoaderState.Loading);
Future.delayed(Duration(seconds: 3), () { //模拟数据调用
_loaderStateModel.changeState(LoaderState.Succeed);
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
/// 初始化加载器的状态
final loaderStateModel =
Provider.of<LoaderStateModel>(context);
if (this._loaderStateModel != loaderStateModel) {
this._loaderStateModel = loaderStateModel;
Future.microtask(() => _getOrderDetailInfo()); //必须要这么做,不然可能会抛出异常,使用Future.microtask执行初始化任务
}
}
@override
Widget build(BuildContext context) => MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: _buildAppBar(),
backgroundColor: AppConstants.backgroundColor,
body: _buildContentView(context),
),
);
/// 构建AppBar视图
Widget _buildAppBar() => AppBar(
backgroundColor: Colors.white,
title: Text('订单详情', style: TextStyle(color: Color(0xff333333))),
centerTitle: true,
elevation: 0.5,
leading: FlatButton(
child: Image.asset(_imageAssetPaths['NavigationBack'],
fit: BoxFit.contain, color: Color(0xff333333)),
padding: const EdgeInsets.all(20),
shape: CircleBorder(),
onPressed: () => finish(),
));
/// 构建页面主视图
Widget _buildContentView(BuildContext context) => Consumer<LoaderStateModel>( //使用Consumer处理共享值
builder: (context, state, _) => LoaderContainer(
loaderState: state.state,
onReload: _getOrderDetailInfo,
contentView: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildOrderHeaderView(),
_buildRefundProcessView(),
_buildReceiverAddressView(),
_buildGoodsInfoContainerView(),
_buildStaticsInfoContentView(),
],
),
),
),
_buildOperatorContainerView(),
],
),
),
);
///...省略一些不重要的代码
}
最后,希望各位学习这个框架的朋友们加油,我也还在更加熟悉这个框架。目前来说,已经尝到这个框架所带来的甜处了。