内容简介:状态主要有:loading,error,empty,以及展示内容的showContentbloc流供baseWidget做状态的变化主要提供了页面状态的Stream,提供子类使用,postPageEmpty2PageContent,postPageError 主要是的页面状态调用方法
状态主要有:loading,error,empty,以及展示内容的showContent
enum PageEnum {
showLoading,
showError,
showEmpty,
showContent,
}
复制代码
1.2 定义一个枚举表示页面状态,另外还需定义事件的类,传递一些必要的数据
bloc流供baseWidget做状态的变化
class PageStatusEvent {
String errorDesc; //错误数据,主要是展示错误页面
bool isRefresh;//主要用于list列表数据
PageEnum pageStatus; 页面状态
PageStatusEvent({this.errorDesc,this.isRefresh, this.pageStatus});
}
复制代码
1.3 BaseBloc封装
class BaseBloc {
void dispose() {
_pageEvent.close();
}
///请求专用的类
Repository repository = new Repository();
///主要是事件通知
BehaviorSubject<PageStatusEvent> _pageEvent =
BehaviorSubject<PageStatusEvent>();
get pageEventSink => _pageEvent.sink;
get pageEventStream => _pageEvent.stream.asBroadcastStream();
postPageEmpty2PageContent(bool isRefresh, Object list) {
pageEventSink.add(new PageStatusEvent(errorDesc : "", isRefresh: true,
pageStatus: ObjectUtil.isEmpty(list)
? PageEnum.showEmpty
: PageEnum.showContent));
}
postPageError(bool isRefresh, String errorMsg) {
pageEventSink.add(
new PageStatusEvent(errorDesc : errorMsg, isRefresh: isRefresh, pageStatus: PageEnum.showError));
}
}
复制代码
主要提供了页面状态的Stream,提供子类使用,postPageEmpty2PageContent,postPageError 主要是的页面状态调用方法
2.BaseWidget封装
@override
Widget build(BuildContext context) {
return _buildWidgetDefault();
}
///构建默认视图
Widget _buildWidgetDefault() {
return WillPopScope(
child: Scaffold(
appBar: buildAppBar(),
body: _buildBody(),
),
);
}
///子类实现,构建各自页面UI控件
Widget buildWidget(BuildContext context);
///构建内容区
Widget _buildBody() {
bloc = BlocProvider.of<B>(context);
return new StreamBuilder(
stream: bloc.pageEventStream,
builder:
(BuildContext context, AsyncSnapshot<PageStatusEvent> snapshot) {
PageStatusEvent status;
bool isShowContent = false;
if (snapshot == null || snapshot.data == null) {
isShowContent = false;
status =
PageStatusEvent(errorDesc : "", isRefresh: true, pageStatus: PageEnum.showLoading);
} else {
status = snapshot.data;
if ((!status.isRefresh) ||
(status.pageStatus == PageEnum.showContent &&
status.isRefresh)) {
isShowContent = true;
} else {
isShowContent = false;
}
}
return Container(
///内容区背景颜色
color: Colours.colorPrimaryWindowBg,
child: Stack(
children: <Widget>[
buildWidget(context),
Offstage(
offstage: isShowContent,
child: getErrorWidget(status),
),
],
),
);
});
}
复制代码
通过pageEventStream 事件来处理页面的状态,默认情况下展示loading状态,通过使用Stack 类似Android中的Framelayout帧布局来初始化loading页面和真正的业务布局。通过isShowContent来控制ErrorWidget视图的展示与否
Widget getErrorWidget(PageStatusEvent status) {
current = status.pageStatus;
if (status != null && status.isRefresh) {
if (status.pageStatus == PageEnum.showEmpty) {
return _buildEmptyWidget();
} else if (status.pageStatus == PageEnum.showError) {
return _buildErrorWidget(status.errorDesc);
} else {
return _buildLoadingWidget();
}
}
return _buildLoadingWidget();
}
复制代码
错误页面的构建,可以自己自定义,详细的代码就不贴不来了,会根据status状态来返回对应的视图
void showLoadSuccess() {
if (current != PageEnum.showContent) {
current = PageEnum.showContent;
//展示内容
bloc.pageEventSink
.add(PageStatusEvent(errorDesc : "", isRefresh: true, pageStatus: PageEnum.showContent));
}
}
void showEmpty() {
if (current != PageEnum.showEmpty) {
current = PageEnum.showEmpty;
//展示空页面
bloc.pageEventSink
.add(PageStatusEvent(errorDesc : "", isRefresh: true, pageStatus: PageEnum.showEmpty));
}
}
void showError() {
if (current != PageEnum.showError) {
current = PageEnum.showError;
//展示错误页面
bloc.pageEventSink
.add(PageStatusEvent(errorDesc : "", isRefresh: true, pageStatus: PageEnum.showError));
}
}
void showLoading() {
if (current != PageEnum.showLoading) {
current = PageEnum.showLoading;
//展示loading页面
bloc.pageEventSink
.add(PageStatusEvent(errorDesc : "", isRefresh: true, pageStatus: PageEnum.showLoading));
}
}
复制代码
另外还需要提供子类调用的四个状态更改的方法
3.bloc页面调用
class BarCodeBloc extends BaseBloc {
final BehaviorSubject<String> _qrCodeController = BehaviorSubject<String>();
get onQrCodeSink => _qrCodeController.sink;
get onQrCodeStream => _qrCodeController.stream;
Future getQrCode(String custId) {
repository.getQrCodeData(custId, onSuccess: (data) {
onQrCodeSink.add(data);
postPageEmpty2PageContent(true, data);
}, onFailure: (error) {
postPageError(true, error.errorDesc);
});
}
@override
void dispose() {
super.dispose();
_qrCodeController.close();
}
}
复制代码
这是一个普通的二维码页面展示,根据api接口返回数据,直接调用postPageEmpty2PageContent用于展示业务布局,以及postPageError展示网络失败的布局
4.基本页面的使用逻辑
@override
Widget buildWidget(BuildContext context) {
return new StreamBuilder(
stream: bloc.onQrCodeStream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new QrImage(
data: snapshot.data,
size: Dimens.dp(200),
),
],
),
),
);
});
}
复制代码
展示其实直接调用就可以了,不需要管理页面相关的状态逻辑,都是在父类帮忙完成了
5.List列表相关的封装
移动端开发很大一部分都是和ListView列表有点关,最好统一封装一下
5.1 上拉加载下拉刷新的控件封装
基于框架 pullToRefresh
//下拉刷新和上拉加载的回调
typedef void OnLoadMore();
typedef void OnRefresh();
class RefreshScaffold extends StatefulWidget {
const RefreshScaffold(
{Key key,
@required this.controller,
this.enablePullUp: true,
this.enablePullDown: true,
this.onRefresh,
this.onLoadMore,
this.child,
this.bottomBar,
this.headerWidget,
this.itemCount,
this.itemBuilder})
: super(key: key);
final RefreshController controller;
final bool enablePullUp;
final bool enablePullDown;
final OnRefresh onRefresh;
final OnLoadMore onLoadMore;
final Widget child;
//底部按钮
final Widget bottomBar;
//固定header的Widget
final PreferredSize headerWidget;
final int itemCount;
final IndexedWidgetBuilder itemBuilder;
@override
State<StatefulWidget> createState() {
return new RefreshScaffoldState();
}
}
/// with AutomaticKeepAliveClientMixin 用于保持列表的状态
class RefreshScaffoldState extends State<RefreshScaffold>
with AutomaticKeepAliveClientMixin {
@override
void initState() {
super.initState();
SchedulerBinding.instance.addPostFrameCallback((_) {
widget.controller.requestRefresh();
});
}
@override
Widget build(BuildContext context) {
super.build(context);
return new Scaffold(
appBar: widget.headerWidget,
body: new SmartRefresher(
controller: widget.controller,
enablePullDown: widget.enablePullDown,
enablePullUp: widget.enablePullUp,
onRefresh: widget.onRefresh,
onLoading: widget.onLoadMore,
footer: ListFooterView(),
header: MaterialClassicHeader(),
child: widget.child ??
new ListView.builder(
itemCount: widget.itemCount,
itemBuilder: widget.itemBuilder,
)),
bottomNavigationBar: widget.bottomBar,
);
}
@override
bool get wantKeepAlive => true;
}
复制代码
主要是增加保持页面状态的wantKeepAlive回调,以及对应的一些页面header或者底部Bottom的封装
5.2 BaseListWidget封装
/// B:对应 BLoc 数据加载的Bloc
/// E: 列表数据Entity
abstract class BaseListState<T extends BaseListWidget, B extends BaseBloc,
E extends Object> extends BaseState<T, B> {
RefreshController controller = new RefreshController();
@override
Widget buildWidget(BuildContext context) {
bloc.pageEventStream.listen((PageStatusEvent event) {
if (event.isRefresh) {
controller.refreshCompleted();
//这句有必要的,实测不加上会导致加载更多无法回调
controller.loadComplete();
} else {
if (event.pageStatus == PageEnum.showEmpty) {
controller.loadNoData();
} else if (event.pageStatus == PageEnum.showError) {
controller.loadFailed();
} else {
controller.loadComplete();
}
}
});
return new StreamBuilder(
stream: blocStream,
builder: (BuildContext context, AsyncSnapshot<List<E>> snapshot) {
return RefreshScaffold(
controller: controller,
enablePullDown: isLoadMore(),
onRefresh: onRefresh,
onLoadMore: onLoadMore,
child: new ListView.builder(
itemCount: snapshot.data == null ? 0 : snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
E model = snapshot.data[index];
return buildItem(model);
},
),
bottomBar: buildBottomBar(),
headerWidget: buildHeaderWidget(),
);
});
}
///默认存在分页
bool isLoadMore() {
return true;
}
///加载数据
get blocStream;
///刷新回调
void onRefresh();
///加载回调
void onLoadMore();
///构建Item
Widget buildItem(E entity);
@override
void onErrorClick() {
super.onErrorClick();
controller.requestRefresh();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
Widget buildBottomBar() {
return null;
}
PreferredSize buildHeaderWidget() {
return null;
}
复制代码
提供三个泛型来控制布局的相关的数据,B对应Bloc,E对应的列表的实体类,提供了blocStream 的Bloc 刷新回调onRefresh,onLoadMore加载更多回调,构建item的回调等
pageEventStream的监听主要处理下来刷新和加载更多的逻辑
bloc.pageEventStream.listen((PageStatusEvent event) {
if (event.isRefresh) {
controller.refreshCompleted();
//这句有必要的,实测不加上会导致加载更多无法回调
controller.loadComplete();
} else {
if (event.pageStatus == PageEnum.showEmpty) {
controller.loadNoData();
} else if (event.pageStatus == PageEnum.showError) {
controller.loadFailed();
} else {
controller.loadComplete();
}
}
});
复制代码
5.3 普通的列表调用方式
只需要实现对应的方法即可,代码就比较清爽了
class _LoanVisitPageState
extends BaseListState<LoanVisitPage, LoanVisitBloc, TaskEntity> {
final String labelId;
_LoanVisitPageState(this.labelId);
@override
void onRefresh() {
bloc.onRefresh(labelId: labelId);
}
@override
void onLoadMore() {
bloc.onLoadMore(labelId: labelId);
}
@override
String setEmptyMsg() {
return Ids.noVisitTask;
}
@override
get blocStream => bloc.loanVisitStream;
@override
Widget buildItem(TaskEntity entity) {
return new LoanVisitItem(entity);
}
}
复制代码
5.4 list普通列表的bloc调用
class LoanVisitBloc extends BaseBloc {
BehaviorSubject<List<TaskEntity>> _loanVisit =
BehaviorSubject<List<TaskEntity>>();
get _loanVisitSink => _loanVisit.sink;
get loanVisitStream => _loanVisit.stream;
List<TaskEntity> _reposList = new List();
int _taskPage = 1;
//列表数据请求
Future getLoanVisitList(String labelId, int page) {
bool isRefresh;
if (page == 1) {
_reposList.clear();
isRefresh = true;
} else {
isRefresh = false;
}
return repository.getVisitList(NetApi.RETURN_VISIT, page, 20,
onSuccess: (list) {
_reposList.addAll(list);
_loanVisitSink.add(UnmodifiableListView<TaskEntity>(_reposList));
postPageEmpty2PageContent(isRefresh, list);
}, onFailure: (error) {
postPageError(isRefresh, error.errorDesc);
});
}
@override
void dispose() {
_loanVisit.close();
}
@override
Future getData({String labelId, int page}) {
return getLoanVisitList(labelId, page);
}
@override
Future onLoadMore({String labelId}) {
_taskPage +=1 ;
return getData(labelId: labelId, page: _taskPage);
}
@override
Future onRefresh({String labelId}) {
_taskPage = 1;
return getData(labelId: labelId, page: 1);
}
}
复制代码
提供刷新和加载更多的方法,也是比较一般的请求
6.总结
借鉴了很多网上的文章,并且结合项目做了修改,bloc的好处避免了setState的损耗,对于页面的状态的管理是很好的,后续会提供一个demo供参考
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 封装JDBC—非框架开发必备的封装类
- Mybatis: 动手封装ORM框架
- 自定义MVC框架-封装模型层
- 优雅地实现Android主流图片加载框架封装,可无侵入切换框架
- 基于spring boot框架进行二次封装,微型框架编写思路
- 自定义MVC框架 -封装控制器层
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Inside Larry's and Sergey's Brain
Richard Brandt / Portfolio / 17 Sep 2009 / USD 24.95
You’ve used their products. You’ve heard about their skyrocketing wealth and “don’t be evil” business motto. But how much do you really know about Google’s founders, Larry Page and Sergey Brin? Inside......一起来看看 《Inside Larry's and Sergey's Brain》 这本书的介绍吧!