基础组件库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

329 lines
10 KiB

  1. import 'dart:async';
  2. import 'package:event_bus/event_bus.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:pull_to_refresh/pull_to_refresh.dart';
  5. import 'package:provider/provider.dart';
  6. import 'package:flutter_bloc/flutter_bloc.dart';
  7. import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_notifier.dart';
  8. import 'package:zhiying_base_widget/widgets/base_state/base_state.dart';
  9. import 'package:zhiying_base_widget/widgets/custom/bottom_pic/bottom_pic.dart';
  10. import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart';
  11. import 'package:zhiying_base_widget/widgets/home/home_auth/home_auth.dart';
  12. import 'package:zhiying_base_widget/widgets/refresh/refresh_header/refresh_gif_header.dart';
  13. import 'package:zhiying_comm/zhiying_comm.dart';
  14. import 'bloc/custom_item_page_bloc.dart';
  15. import 'bloc/custom_item_page_state.dart';
  16. import 'bloc/custom_item_page_event.dart';
  17. import 'bloc/custom_item_page_repository.dart';
  18. import 'package:zhiying_base_widget/pages/custom_page/event/reload_event.dart';
  19. ///
  20. /// 通用模块的分类导航下的子模块
  21. ///
  22. ///
  23. class CustomItemPage extends StatelessWidget {
  24. final Map<String, dynamic> data;
  25. final int tabIndex;
  26. final String modPid;
  27. final String modId;
  28. final bool needBuildStatus;
  29. Function(double) scroller;
  30. CustomItemPage(this.data, this.tabIndex, this.modId, this.modPid, this.needBuildStatus, {this.scroller});
  31. @override
  32. Widget build(BuildContext context) {
  33. return MultiProvider(
  34. providers: [
  35. ChangeNotifierProvider.value(value: MainPageNotifier()),
  36. ],
  37. child: BlocProvider<CustomItemPageBloc>(
  38. create: (_) => CustomItemPageBloc(CustomItemPageRepository(this.data, this.tabIndex, this.modId, this.modPid)),
  39. child: _CustomItemPageContainer(
  40. this.data,
  41. this.tabIndex,
  42. this.modId,
  43. this.modPid,
  44. this.needBuildStatus,
  45. scroller: this.scroller,
  46. ),
  47. ),
  48. );
  49. }
  50. }
  51. class _CustomItemPageContainer extends StatefulWidget {
  52. final Map<String, dynamic> data;
  53. final int tabIndex;
  54. final String modPid;
  55. final String modId;
  56. final bool needBuildStatus;
  57. final Function(double) scroller;
  58. const _CustomItemPageContainer(this.data, this.tabIndex, this.modId, this.modPid, this.needBuildStatus, {this.scroller, Key key}) : super(key: key);
  59. @override
  60. __CustomItemPageContainerState createState() => __CustomItemPageContainerState();
  61. }
  62. class __CustomItemPageContainerState extends BasePageState<_CustomItemPageContainer> {
  63. @override
  64. bool get wantKeepAlive => true;
  65. ScrollController _controller;
  66. RefreshController _refreshController;
  67. EventBus _eventBus;
  68. /// 回到顶点
  69. void _scrollTop() {
  70. // _controller.jumpTo(0);
  71. _controller.animateTo(0, duration: Duration(milliseconds: 200), curve: Curves.linear);
  72. }
  73. /// 初始化
  74. void _initEvent() {
  75. BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageInitEvent());
  76. }
  77. /// 刷新
  78. void _refreshEvent() {
  79. BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageRefreshEvent());
  80. }
  81. /// 下拉更多
  82. void _loadEvent() {
  83. // BlocProvider.of<CustomItemPageBloc>(context).add(CustomItemPageLoadEvent());
  84. Provider.of<MainPageNotifier>(context, listen: false).loadMore();
  85. Future.delayed(Duration(milliseconds: 500), () => _refreshController?.loadComplete());
  86. }
  87. @override
  88. void initState() {
  89. _controller = ScrollController();
  90. _eventBus = EventBus();
  91. _refreshController = RefreshController(initialRefresh: false);
  92. _initEvent();
  93. _controller.addListener(() {
  94. if (widget.scroller != null) {
  95. widget?.scroller(_controller.offset);
  96. }
  97. });
  98. super.initState();
  99. }
  100. @override
  101. void dispose() {
  102. _controller?.dispose();
  103. _refreshController?.dispose();
  104. _eventBus?.destroy();
  105. super.dispose();
  106. }
  107. @override
  108. Widget buildX(BuildContext context) {
  109. return BlocConsumer<CustomItemPageBloc, CustomItemPageState>(
  110. listener: (context, state) {},
  111. buildWhen: (prev, current) {
  112. /// 子Item刷新
  113. if (current is CustomItemPageItemWidgetRefreshState) {
  114. /// 子Widget刷新
  115. refreshPage();
  116. return false;
  117. }
  118. /// 下拉刷新成功
  119. if (current is CustomItemPageRefreshSuccessState) {
  120. _refreshController?.refreshCompleted(resetFooterState: true);
  121. return false;
  122. }
  123. /// 下拉刷新出错
  124. if (current is CustomItemPageRefreshErrorState) {
  125. _refreshController?.refreshFailed();
  126. return false;
  127. }
  128. /// 上拉加载数据成功
  129. if (current is CustomItemPageLoadSuccessState) {
  130. _refreshController?.loadComplete();
  131. return false;
  132. }
  133. /// 上拉加载数据出错
  134. if (current is CustomItemPageLoadErrorState) {
  135. _refreshController?.loadNoData();
  136. return false;
  137. }
  138. /// 上拉加载没有更多数据
  139. if (current is CustomItemPageNoMoreDataState) {
  140. _refreshController?.loadNoData();
  141. return false;
  142. }
  143. /// 数据加载出错
  144. if (current is CustomItemPageErrorState) {
  145. return false;
  146. }
  147. if (current is CustomItemPageAppRestartState) {
  148. // 重启App
  149. Logger.log('开始重新启动App CustomItemPageAppRestartState');
  150. EventUtil.instance.fire(ReloadEvent());
  151. return false;
  152. }
  153. return true;
  154. },
  155. builder: (context, state) {
  156. Logger.log('Custom item page builder 刷新了');
  157. if (state is CustomItemPageLoadedState) {
  158. Logger.log('custom item page current state = ' + state?.toString());
  159. if (EmptyUtil.isEmpty(state.model))
  160. return _buildEmptyWidget();
  161. else
  162. return _buildMainWidget(state.model);
  163. }
  164. if (state is CustomItemPageInitErrorState) {
  165. return _buildEmptyWidget();
  166. }
  167. return _buildSkeletonWidget();
  168. },
  169. );
  170. }
  171. /// 有数据
  172. Widget _buildMainWidget(final List<Map<String, dynamic>> model) {
  173. return MediaQuery.removePadding(
  174. context: context,
  175. removeTop: false,
  176. child: Stack(
  177. children: <Widget>[
  178. SmartRefresher(
  179. controller: _refreshController,
  180. enablePullDown: true,
  181. enablePullUp: _hasProductList(model),
  182. onRefresh: _refreshEvent,
  183. onLoading: _loadEvent,
  184. header: RefreshGifHeader(),
  185. // footer: RefreshFooter(),
  186. child: CustomScrollView(
  187. physics: BouncingScrollPhysics(),
  188. controller: _controller,
  189. slivers: _buildContentWidgets(model),
  190. ),
  191. ),
  192. Align(
  193. alignment: Alignment.bottomCenter,
  194. child: Container(
  195. margin: const EdgeInsets.only(bottom: 8),
  196. child: _buildAuthWidget(model),
  197. ), //_buildAuthWidget(model),
  198. ),
  199. Align(
  200. alignment: Alignment.bottomCenter,
  201. child: Container(
  202. margin: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
  203. child: _buildBottomPic(model),
  204. ), //_buildAuthWidget(model),
  205. )
  206. ],
  207. ),
  208. );
  209. }
  210. /// 根据widget的modName生成视图
  211. List<Widget> _buildContentWidgets(final List<Map<String, dynamic>> datas) {
  212. List<Widget> result = [];
  213. for (int i = 0; i < datas.length; i++) {
  214. WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[i]));
  215. if (item.modName == 'audit_tip' || item.modName == "bottom_suspension") {
  216. Logger.debug('授权组件,跳过');
  217. continue;
  218. }
  219. if (item.modName == 'product') {
  220. datas[i]['eventBus'] = _eventBus;
  221. }
  222. result.addAll(WidgetFactory.create(
  223. item.modName,
  224. isSliver: true,
  225. model: datas[i],
  226. ));
  227. }
  228. // if (widget.needBuildStatus) {
  229. // double top = MediaQueryData.fromWindow(window).padding.top;
  230. // result.insert(
  231. // 0,
  232. // SliverPersistentHeader(
  233. // pinned: false,
  234. // floating: false,
  235. // delegate: CustomSliverPersistentHeaderDelegate(max: top, min: top, child: Container(
  236. // // color: Colors.yellow,
  237. // color: HexColor.fromHex('#FFFF4242'),
  238. // )),
  239. // ));
  240. //
  241. // }
  242. return result;
  243. }
  244. /// 判断是否有商品列表,如果没有不进行,上拉更多
  245. bool _hasProductList(final List<Map<String, dynamic>> datas) {
  246. var rlt = false;
  247. if (!EmptyUtil.isEmpty(datas)) {
  248. for (int i = 0; i < datas.length; i++) {
  249. WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[i]));
  250. if (item.modName == 'product') {
  251. rlt = true;
  252. break;
  253. }
  254. }
  255. }
  256. return rlt;
  257. }
  258. /// 空数据
  259. Widget _buildEmptyWidget() {
  260. return SmartRefresher(
  261. controller: _refreshController,
  262. onRefresh: _refreshEvent,
  263. enablePullDown: true,
  264. child: Container(
  265. child: EmptyWidget(),
  266. ),
  267. );
  268. }
  269. /// 骨架图
  270. Widget _buildSkeletonWidget() {
  271. return Container();
  272. }
  273. /// 特殊的授权组件
  274. Widget _buildAuthWidget(final List<Map<String, dynamic>> datas) {
  275. int length = datas?.length ?? 0;
  276. if (length == 0) return Container();
  277. Widget rlt;
  278. for (int i = 0; i < datas.length; i++) {
  279. WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[i]));
  280. if (item.modName == 'audit_tip') {
  281. rlt = HomeAuth(datas[i]);
  282. break;
  283. }
  284. }
  285. return rlt ?? Container();
  286. }
  287. /// 特殊的底部图片跳转
  288. Widget _buildBottomPic(final List<Map<String, dynamic>> datas) {
  289. int length = datas?.length ?? 0;
  290. if (length == 0) return Container();
  291. Widget rlt;
  292. for (int i = 0; i < datas.length; i++) {
  293. WidgetModel item = WidgetModel.fromJson(Map<String, dynamic>.from(datas[i]));
  294. if (item.modName == 'bottom_suspension') {
  295. rlt = BottomPic(model: datas[i]);
  296. break;
  297. }
  298. }
  299. return rlt ?? Container();
  300. }
  301. }