import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.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/model/background_model.dart'; import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; import 'package:zhiying_base_widget/widgets/empty/empty_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/background_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 'package:zhiying_base_widget/widgets/search/widget/my_tab.dart'; import 'dart:ui'; /// /// 通用模块页面 /// 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) { Logger.log("数据: " + widget?.data.toString()); 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, AutomaticKeepAliveClientMixin { TabController _tabController; // 是否有AppBar bool _isHasAppbar = false; // 是否有TabBar bool _isHasTabBar = false; double backgroundTopMargin = 0; BackgroundBloc backgroundBloc; /// 刷新 void _onRefreshEvent() async { BlocProvider.of(context).add(CustomPageRefreshEvent()); } @override void initState() { backgroundBloc = BackgroundBloc(); super.initState(); } @override void dispose() { _tabController?.dispose(); backgroundBloc.streamController.close(); super.dispose(); } @override Widget build(BuildContext context) { SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark); return 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(); Logger.log("通用模板数据", state.model); return _buildMainWidget(state.model, state.backgroundModel); } /// 初始化失败 if (state is CustomPageInitErrorState) { return _buildEmptyWidget(); } /// 骨架图 return _buildSkeletonWidget(); }, ); } /// 有数据 Widget _buildMainWidget(List> model, BackgroundModel backgroundModel) { return Scaffold( appBar: _buildAppbar(model?.first), backgroundColor: HexColor.fromHex(backgroundModel?.bgColor == null || backgroundModel?.bgColor == '' ? "#FFF9F9F9" : backgroundModel?.bgColor ?? "#FFF9F9F9"), // floatingActionButton: _buildFloatWidget(), floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100), body: Stack( children: [ _buildBackground(backgroundModel), Column(children: _buildFirstWidget(model, backgroundModel)), ], ), ); } /// 骨架图 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, BackgroundModel backgroundModel) { 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) { result.insert(0, SizedBox(height: MediaQueryData.fromWindow(window).padding.top, child: Container(color: HexColor.fromHex(backgroundModel?.headerBg?.mainColor)))); } 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(!EmptyUtil.isEmpty(data) ? data['app_bar_bg_color'] ?? '#FFFFFF' : '#FFFFFF'), brightness: Brightness.light, leading: Navigator.canPop(context) ? IconButton( icon: Icon( Icons.arrow_back_ios, size: 22, color: HexColor.fromHex(!EmptyUtil.isEmpty(data) ? data['app_bar_name_color'] ?? '#333333' : '#333333'), ), onPressed: () => Navigator.maybePop(context), ) : null, title: Text( !EmptyUtil.isEmpty(data) && data.containsKey('app_bar_name') // ? data['app_bar_name'] != '自定义页面' ? data['app_bar_name'] // : parentTitle : parentTitle, style: TextStyle( color: HexColor.fromHex(!EmptyUtil.isEmpty(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), scroller: _listenScroller, ), )); return result; } // 2、导航栏开启的情况 if (listStyle.length > 0) { // 获取分类类型,如果第一位type等于imageAndText说明是带有小图标的 bool haveIcon = false; try { haveIcon = listStyle[0]['type']?.toString() == 'imageAndText'; } catch (e, s){ Logger.error(e, s); } // tabContorller 初始化 if (null == _tabController || _tabController.length != listStyle.length) { _tabController = new TabController(length: listStyle.length, vsync: this); } result.add(Container( height: 40, padding: const EdgeInsets.only(bottom: 5), 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: !haveIcon ? listStyle.map((e) => Text(e['name'])).toList() : // 带图标 listStyle.map((item) { return MyTab( icon: CachedNetworkImage( imageUrl: item['choose_image_url'] ?? '', width: 14, ), text: item['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), scroller: _listenScroller, )); } return result; } /// 背景颜色 _buildBackground(BackgroundModel backgroundModel) { if (backgroundModel != null) { var headerBg = backgroundModel.headerBg; return StreamBuilder( stream: backgroundBloc.outData, builder: (context, asncy) { return Container( constraints: BoxConstraints(minHeight: 0), height: (double.tryParse(headerBg?.height) ?? 0) + backgroundTopMargin ?? 0, width: double.infinity, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [HexColor.fromHex(headerBg?.mainColor ?? ""), HexColor.fromHex(headerBg?.assistColor ?? ""), HexColor.fromHex(headerBg?.minorColor ?? "")])), ); }, ); } else { return Container(); } } // /// 悬浮按钮 // Widget _buildFloatWidget() { // return Visibility( // visible: true, // child: GestureDetector( // onTap: () => _scrollTop(), // behavior: HitTestBehavior.opaque, // child: Container( // height: 30, // width: 30, // child: Icon(Icons.arrow_upward), // ), // ), // ); // } ///监听页面滚动 _listenScroller(double offset) { if (offset >= 0) { backgroundTopMargin = -offset; if (backgroundTopMargin > -500) { backgroundBloc.streamController.add(""); } } } @override bool get wantKeepAlive => true; } /// 回到顶部的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); } }