import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:tab_indicator_styler/tab_indicator_styler.dart'; import 'package:zhiying_base_widget/pages/custom_page/custom_item_page.dart'; import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; import 'package:zhiying_base_widget/widgets/custom/search/custom_search_widget.dart'; import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart'; import 'package:zhiying_base_widget/widgets/others/mine_header_bg_widget.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'bloc/custom_page_bloc.dart'; import 'bloc/custom_page_state.dart'; import 'bloc/custom_page_event.dart'; import 'bloc/custom_page_repository.dart'; import 'dart:ui'; import 'package:fluttertoast/fluttertoast.dart'; /// /// 通用模块页面 /// class CustomPage extends StatefulWidget { final Map data; CustomPage(this.data, {Key key}) : super(key: key); @override _CustomPageState createState() => _CustomPageState(); } class _CustomPageState extends State { @override Widget build(BuildContext context) { return MultiProvider( providers: [ ChangeNotifierProvider.value(value: MainPageBgNotifier()), ], child: BlocProvider( create: (_) => CustomPageBloc(CustomPageRepository(data: widget?.data))..add(CustomPageInitEvent()), child: _CommonPageContainer(widget?.data), // ), ), ); } } class _CommonPageContainer extends StatefulWidget { final Map data; _CommonPageContainer(this.data); @override __CommonPageContainerState createState() => __CommonPageContainerState(); } class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin { TabController _tabController; // 是否有AppBar bool _isHasAppbar = false; // 是否有TabBar bool _isHasTabBar = false; /// 刷新 void _onRefreshEvent() async { BlocProvider.of(context).add(CustomPageRefreshEvent()); } @override void dispose() { _tabController?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return MediaQuery.removePadding( context: context, child: BlocConsumer( listener: (context, state) {}, buildWhen: (prev, current) { if (current is CustomPageErrorState) { return false; } if (current is CustomPageRefreshSuccessState) { // _refreshController.refreshCompleted(resetFooterState: true); return false; } if (current is CustomPageRefreshErrorState) { // _refreshController.refreshFailed(); return false; } return true; }, builder: (context, state) { /// 有数据 if (state is CustomPageLoadedState) { if (EmptyUtil.isEmpty(state.model)) return _buildEmptyWidget(); return _buildMainWidget(state.model); } /// 初始化失败 if (state is CustomPageInitErrorState) { return _buildEmptyWidget(); } /// 骨架图 return _buildSkeletonWidget(); }, ), ); } /// 有数据 Widget _buildMainWidget(List> model) { return Stack( children: [ Scaffold( appBar: _buildAppbar(model?.first), backgroundColor: HexColor.fromHex('#F9F9F9'), // floatingActionButton: _buildFloatWidget(), floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100), body: Column(children: _buildFirstWidget(model)), ), ], ); } /// 骨架图 Widget _buildSkeletonWidget() { return Scaffold(); } /// 空数据视图 Widget _buildEmptyWidget() { return Scaffold( backgroundColor: HexColor.fromHex('#F9F9F9'), appBar: AppBar( brightness: Brightness.light, backgroundColor: Colors.white, leading: IconButton( icon: Icon( Icons.arrow_back_ios, size: 22, color: HexColor.fromHex('#333333'), ), onPressed: () => Navigator.maybePop(context), ), title: Text( '', style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 18, fontWeight: FontWeight.bold), ), centerTitle: true, elevation: 0, ), body: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ Align( alignment: Alignment.topCenter, child: EmptyWidget( tips: '网络似乎开小差了~', ), ), GestureDetector( onTap: () => _onRefreshEvent(), behavior: HitTestBehavior.opaque, child: Container( alignment: Alignment.center, decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), border: Border.all(color: HexColor.fromHex('#999999'), width: 0.5), color: Colors.white), width: 80, height: 20, child: Text( '刷新一下', style: TextStyle(fontSize: 12, color: HexColor.fromHex('#333333'), fontWeight: FontWeight.bold), ), ), ) ], )); } /// 数据,生成第一层widget List _buildFirstWidget(List> model) { List result = []; // 分类导航的key ⚠️ 这里先写成Test 后续要改 const String CATEGORY_KEY = 'category'; // 判断是否有分类导航 // 判断最后一个是否属于分类导航,如果属于,则有分类导航,如果不是,则无分类导航 bool haveCategory = !EmptyUtil.isEmpty(model?.last) && model.last.containsKey('mod_name') && model.last['mod_name'] == CATEGORY_KEY; int endIndexLength = model.length; // 如果没有分类导航,则取分类导航之上的所有mod if (!haveCategory) { for (int i = 0; i < model.length; i++) { Map item = model[i]; if (item['mod_name'] == CATEGORY_KEY) { endIndexLength = (i + 1); break; } } } for (int i = 0; i < endIndexLength; i++) { WidgetModel item = WidgetModel.fromJson(Map.from(model[i])); // last model if (i == endIndexLength - 1) { result.addAll(_buildTabBar(model[i], i)); break; } // appBar 无需在这里添加 if (item.modName.contains('appbar')) { continue; } result.addAll(WidgetFactory.create(item.modName, isSliver: false, model: model[i])); } // 没有appbar并且没有tabbar,则给第一个元素加边距 if (!_isHasAppbar && !_isHasTabBar) { result.insert(0, SizedBox(height: MediaQueryData.fromWindow(window).padding.top, child: Container(color: HexColor.fromHex('#FF4242'),),)); } return result; } /// appbar Widget _buildAppbar(final Map model) { if (EmptyUtil.isEmpty(model)) return null; String mobName = model['mod_name']; if (!mobName.contains('_appbar')) return null; Map data = Map(); try { data = jsonDecode(model['data']); } catch (e, s) { Logger.warn(e, s); } String parentTitle = !EmptyUtil.isEmpty(widget?.data) ? widget?.data['title'] ?? '' : ''; _isHasAppbar = true; return AppBar( backgroundColor: HexColor.fromHex(null != data ? data['app_bar_bg_color'] ?? '#FFFFFF' : '#FFFFFF'), brightness: Brightness.light, leading: IconButton( icon: Icon( Icons.arrow_back_ios, size: 22, color: HexColor.fromHex('#333333'), ), onPressed: () => Navigator.maybePop(context), ), title: Text( null != data && data.containsKey('app_bar_name') ? data['app_bar_name'] != '自定义页面' ? data['app_bar_name'] : parentTitle : parentTitle, style: TextStyle( color: HexColor.fromHex(null != data ? data['app_bar_name_color'] ?? '#333333' : '#333333'), fontSize: 16, fontWeight: FontWeight.bold, ), ), centerTitle: true, elevation: 0, ); } /// tabBar List _buildTabBar(final Map model, final int index) { Map data = Map(); List> listStyle = []; List result = []; try { data = jsonDecode(model['data']); listStyle = List.from(data['list_style']); } catch (e, s) { Logger.warn(e, s); } // 1、导航栏没开启的情况 传null进去进行获取没开启导航栏的widget集合 if (EmptyUtil.isEmpty(listStyle)) { result.add(Expanded( child: CustomItemPage(null, 0, model['mod_id']?.toString() ?? null, model['mod_pid']?.toString() ?? null, (!_isHasAppbar && index == 0 )), )); return result; } // 2、导航栏开启的情况 if (listStyle.length > 0) { // tabContorller 初始化 if (null == _tabController || _tabController.length != listStyle.length) { _tabController = new TabController(length: listStyle.length, vsync: this); } result.add(Container( height: 40, width: double.infinity, color: HexColor.fromHex(data['bg_color']), child: TabBar( controller: _tabController, isScrollable: /*listStyle.length <= 5 ? false : */ true, labelColor: HexColor.fromHex(data['choose_text_color'] ?? '#FF4242'), labelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), unselectedLabelColor: HexColor.fromHex(data['text_color'] ?? '#999999'), unselectedLabelStyle: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), indicatorSize: TabBarIndicatorSize.label, indicator: MaterialIndicator( color: HexColor.fromHex(data['choose_color'] ?? '#FF4242'), bottomLeftRadius: 1.25, topLeftRadius: 1.25, topRightRadius: 1.25, bottomRightRadius: 1.25, height: 2.5, horizontalPadding: 5, ), tabs: listStyle.map((e) => Text(e['name'])).toList(), ), )); _isHasTabBar = true; // 最后添加TabBarView result.add(Expanded( child: TabBarView( controller: _tabController, children: _buildTabBarViewChildren(listStyle, model['mod_id']?.toString(), model['mod_pid']?.toString(), index), ), )); } return result; } /// 返回TabBarView的视图 List _buildTabBarViewChildren(final List> listStyle, final String modId, final String modPid, final int index) { List result = []; for (int i = 0; i < listStyle.length; i++) { result.add(CustomItemPage(listStyle[i], i, modId, modPid, (!_isHasAppbar && !_isHasTabBar && index == 0 ))); } return result; } // /// 悬浮按钮 // Widget _buildFloatWidget() { // return Visibility( // visible: true, // child: GestureDetector( // onTap: () => _scrollTop(), // behavior: HitTestBehavior.opaque, // child: Container( // height: 30, // width: 30, // child: Icon(Icons.arrow_upward), // ), // ), // ); // } } /// 回到顶部的icon class _CustomFloatingActionButtonLocation extends FloatingActionButtonLocation { FloatingActionButtonLocation location; double offsetX; // X方向的偏移量 double offsetY; // Y方向的偏移量 _CustomFloatingActionButtonLocation(this.location, this.offsetX, this.offsetY); @override Offset getOffset(ScaffoldPrelayoutGeometry scaffoldGeometry) { Offset offset = location.getOffset(scaffoldGeometry); return Offset(offset.dx + offsetX, offset.dy + offsetY); } }