From f4e304e9e0f09311352de7fa40672214e1432a98 Mon Sep 17 00:00:00 2001 From: PH2 <1293456824@qq.com> Date: Sat, 14 Nov 2020 10:02:34 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E5=95=86=E5=93=81=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E7=9A=84=E8=BD=AE=E6=92=AD=E5=9B=BE=E6=95=B0=E6=8D=AE=E4=BC=98?= =?UTF-8?q?=E5=8C=96=202=E3=80=81=E6=94=B6=E8=97=8F=E5=A4=B9=E9=A6=96?= =?UTF-8?q?=E6=AC=A1=E6=89=93=E5=BC=80=E9=BB=91=E8=89=B2=E9=97=AE=E9=A2=98?= =?UTF-8?q?=203=E3=80=81=E5=8F=8D=E9=A6=88=E7=9A=84=E5=81=87=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=204=E3=80=81=E5=95=86=E5=93=81=E5=88=86=E4=BA=AB?= =?UTF-8?q?=E7=9A=84=E8=8E=B7=E5=8F=96=E6=95=B0=E6=8D=AE=E9=97=AE=E9=A2=98?= =?UTF-8?q?=205=E3=80=81=E6=90=9C=E7=B4=A2=E7=BB=93=E6=9E=9C=E9=A1=B5=20?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E9=A1=B5=E9=9D=A2=E7=9A=84=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=A0=8F=E5=AD=97=E4=BD=93=E9=A2=9C=E8=89=B2=206=E3=80=81?= =?UTF-8?q?=E9=80=80=E5=87=BA=E7=99=BB=E5=BD=95=E8=BF=94=E5=9B=9E=E4=B8=8A?= =?UTF-8?q?=E4=B8=80=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/android/app/src/main/res/raw/safe.jpg | 1 + example/lib/main.dart | 3 + .../custom_page/bloc/custom_page_bloc.dart | 61 ++++ .../custom_page/bloc/custom_page_event.dart | 10 + .../bloc/custom_page_repository.dart | 31 ++ .../custom_page/bloc/custom_page_state.dart | 25 ++ lib/pages/custom_page/custom_page.dart | 286 +++++++++++++++ .../custom_page/custom_page_skeleton.dart | 8 + lib/pages/favorites_page/favorites_page.dart | 1 + .../feedback_page/bloc/feedback_bloc.dart | 36 ++ .../feedback_page/bloc/feedback_event.dart | 13 + .../bloc/feedback_repository.dart | 30 ++ .../feedback_page/bloc/feedback_state.dart | 15 + lib/pages/feedback_page/feedback_page.dart | 332 +++++++++++++----- .../feedback_page/feedback_record_page.dart | 12 +- .../feedback_page/model/feedback_model.dart | 194 ++++++++++ .../bloc/goods_details_page_repository.dart | 14 +- lib/pages/search_page/bloc/search_bloc.dart | 10 +- .../search_page/bloc/search_repository.dart | 33 +- lib/pages/search_page/search_page.dart | 16 +- .../search_result_page.dart | 16 +- lib/pages/setting_page/setting_page.dart | 5 +- lib/register.dart | 10 +- .../custom/appbar/custom_appbar_creater.dart | 16 + .../custom/appbar/custom_appbar_widget.dart | 45 +++ .../custom/search/custom_search_widget.dart | 13 + .../custom/tabbar/custom_tabbar_widget.dart | 13 + .../goods_details/coupon/counpon_widget.dart | 32 +- .../coupon/model/counpon_model.dart | 40 +++ .../footer/goods_details_footer_widget.dart | 230 +++++------- .../model/goods_details_footer_model.dart | 47 +++ .../home_quick_entry/home_quick_entry.dart | 140 +++++--- .../home/home_sreach/home_sreach_widget.dart | 10 +- lib/widgets/share/share_alert.dart | 2 +- .../wallet_bil_detail/wallet_bil_sk.dart | 23 +- 35 files changed, 1385 insertions(+), 388 deletions(-) create mode 100755 example/android/app/src/main/res/raw/safe.jpg create mode 100644 lib/pages/custom_page/bloc/custom_page_bloc.dart create mode 100644 lib/pages/custom_page/bloc/custom_page_event.dart create mode 100644 lib/pages/custom_page/bloc/custom_page_repository.dart create mode 100644 lib/pages/custom_page/bloc/custom_page_state.dart create mode 100644 lib/pages/custom_page/custom_page.dart create mode 100644 lib/pages/custom_page/custom_page_skeleton.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_bloc.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_event.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_repository.dart create mode 100644 lib/pages/feedback_page/bloc/feedback_state.dart create mode 100644 lib/pages/feedback_page/model/feedback_model.dart create mode 100644 lib/widgets/custom/appbar/custom_appbar_creater.dart create mode 100644 lib/widgets/custom/appbar/custom_appbar_widget.dart create mode 100644 lib/widgets/custom/search/custom_search_widget.dart create mode 100644 lib/widgets/custom/tabbar/custom_tabbar_widget.dart diff --git a/example/android/app/src/main/res/raw/safe.jpg b/example/android/app/src/main/res/raw/safe.jpg new file mode 100755 index 0000000..794419f --- /dev/null +++ b/example/android/app/src/main/res/raw/safe.jpg @@ -0,0 +1 @@ +áƒEÓ3}jB\²Ž^Q‘ \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index fc003a7..eee27ff 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,6 +9,7 @@ import 'package:zhiying_comm/zhiying_comm.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:pull_to_refresh/pull_to_refresh.dart'; import 'package:zhiying_comm/util/update/app_update_util.dart'; +import 'package:jdsdk/jdsdk.dart'; import 'util/localizations_delegate.dart'; @@ -45,6 +46,8 @@ class _MyAppState extends State { FlutterAlibc.initAlibc(version: "", appName: "").then((result) { print("白å·" + '${result.errorCode} ${result.errorMessage}'); }); + print('åˆå§‹åŒ–京东~'); + Jdsdk.init(appKey: 'c0abbe1f201464ee139d613591a1be02', appSecret: 'a0eeac7105bf4c7cb573972aa03ed95c'); // appæ›´æ–°æ’ä»¶ AppUpdateUtil.initXUpdate(); } diff --git a/lib/pages/custom_page/bloc/custom_page_bloc.dart b/lib/pages/custom_page/bloc/custom_page_bloc.dart new file mode 100644 index 0000000..185272e --- /dev/null +++ b/lib/pages/custom_page/bloc/custom_page_bloc.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'custom_page_event.dart'; +import 'custom_page_repository.dart'; +import 'custom_page_state.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; + +class CustomPageBloc extends Bloc { + // CommonBloc() : super(CommonInitial()); + + CustomPageRepository repository; + + CustomPageBloc(this.repository); + + @override + CustomPageState get initialState => CustomPageInitialState(); + + @override + Stream mapEventToState( + CustomPageEvent event, + ) async* { + /// åˆå§‹åŒ– + if (event is CustomPageInitEvent) { + yield* _mapInitToState(event); + } + + /// 刷新 + if (event is CustomPageRefreshEvent) { + yield* _mapRefreshToState(event); + } + } + + /// 刷新 + Stream _mapRefreshToState(CustomPageRefreshEvent event) async* { + var result = await repository.fetchInitData(); + if (!EmptyUtil.isEmpty(result)) { + yield CustomPageRefreshSuccessState(); + yield CustomPageLoadedState(model: result); + } else { + yield CustomPageRefreshErrorState(); + yield CustomPageErrorState(); + } + } + + /// åˆå§‹åŒ– + Stream _mapInitToState(CustomPageInitEvent event) async* { + // 获å–ç¼“å­˜æ•°æ® + var cache = await repository.fetchCacheData(); + if (!EmptyUtil.isEmpty(cache) && cache is List) { + yield CustomPageLoadedState(model: cache); + } + // 获å–ç½‘ç»œæ•°æ® + var result = await repository.fetchInitData(); + if (!EmptyUtil.isEmpty(result) && result is List) { + yield CustomPageLoadedState(model: result); + } else { + yield CustomPageInitErrorState(); + } + } +} diff --git a/lib/pages/custom_page/bloc/custom_page_event.dart b/lib/pages/custom_page/bloc/custom_page_event.dart new file mode 100644 index 0000000..fd8fa28 --- /dev/null +++ b/lib/pages/custom_page/bloc/custom_page_event.dart @@ -0,0 +1,10 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class CustomPageEvent {} + +/// åˆå§‹åŒ– +class CustomPageInitEvent extends CustomPageEvent{} + +/// 刷新 +class CustomPageRefreshEvent extends CustomPageEvent{} diff --git a/lib/pages/custom_page/bloc/custom_page_repository.dart b/lib/pages/custom_page/bloc/custom_page_repository.dart new file mode 100644 index 0000000..dbeea7b --- /dev/null +++ b/lib/pages/custom_page/bloc/custom_page_repository.dart @@ -0,0 +1,31 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; + +class CustomPageRepository { + Map data; + + CustomPageRepository({this.data}); + + /// åˆå§‹åŒ– + Future>> fetchInitData() async { + try { + String skipIdentifier = !EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) + ? data[GlobalConfig.SKIP_IDENTIFIER] + : null; + if (!EmptyUtil.isEmpty(skipIdentifier)) { + var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {} + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// ç¼“å­˜æ•°æ® + Future>> fetchCacheData() async { + try {} catch (e, s) { + Logger.error(e, s); + } + return null; + } +} diff --git a/lib/pages/custom_page/bloc/custom_page_state.dart b/lib/pages/custom_page/bloc/custom_page_state.dart new file mode 100644 index 0000000..3f1bc93 --- /dev/null +++ b/lib/pages/custom_page/bloc/custom_page_state.dart @@ -0,0 +1,25 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class CustomPageState {} + +/// åˆå§‹åŒ– +class CustomPageInitialState extends CustomPageState {} + +/// æ•°æ®åŠ è½½æˆåŠŸ +class CustomPageLoadedState extends CustomPageState { + List> model; + CustomPageLoadedState({this.model}); +} + +/// 刷新æˆåŠŸ +class CustomPageRefreshSuccessState extends CustomPageState{} + +/// 刷新失败 +class CustomPageRefreshErrorState extends CustomPageState{} + +/// åˆå§‹åŒ–失败 +class CustomPageInitErrorState extends CustomPageState {} + +/// æ•°æ®åŠ è½½å¤±è´¥ +class CustomPageErrorState extends CustomPageState {} diff --git a/lib/pages/custom_page/custom_page.dart b/lib/pages/custom_page/custom_page.dart new file mode 100644 index 0000000..03930a0 --- /dev/null +++ b/lib/pages/custom_page/custom_page.dart @@ -0,0 +1,286 @@ +import 'package:flutter/cupertino.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart'; +import 'package:zhiying_base_widget/widgets/refresh/refresh_header/refresh_header.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:zhiying_comm/util/custom_sliver_persistent_header_delegate.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'; + +/// +/// 通用模å—é¡µé¢ +/// +class CustomPage extends StatelessWidget { + final Map data; + + CustomPage(this.data, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + // return MultiProvider( + // providers: [], + // child: + return BlocProvider( + create: (_) => CustomPageBloc(CustomPageRepository(data: data))..add(CustomPageInitEvent()), + child: _CommonPageContainer(), + // ), + ); + } +} + +class _CommonPageContainer extends StatefulWidget { + @override + __CommonPageContainerState createState() => __CommonPageContainerState(); +} + +class __CommonPageContainerState extends State<_CommonPageContainer> with SingleTickerProviderStateMixin { + ScrollController _controller; + RefreshController _refreshController; + TabController _tabController; + bool _isEnded = false; + + /// 回到顶点 + void _scrollTop() { + // _controller.jumpTo(0); + _controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.linear); + } + + /// 刷新 + void _onRefreshEvent() async { + BlocProvider.of(context).add(CustomPageRefreshEvent()); + } + + /// 加载更多 + void _onLoadEvent() async {} + + @override + void initState() { + _controller = ScrollController(); + _refreshController = RefreshController(initialRefresh: false); + _tabController = TabController(length: 10, vsync: this); + super.initState(); + } + + @override + void dispose() { + _controller?.dispose(); + _refreshController?.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) { + return _buildMainWidget(); + } + if (state is CustomPageInitErrorState) { + return _buildMainWidget(); + } + return _buildSkeletonWidget(); + }, + ), + ); + } + + /// æœ‰æ•°æ® + Widget _buildMainWidget() { + double top = MediaQueryData.fromWindow(window).padding.top; + return Scaffold( + backgroundColor: HexColor.fromHex('#F9F9F9'), + floatingActionButton: _buildFloatWidget(), + floatingActionButtonLocation: _CustomFloatingActionButtonLocation(FloatingActionButtonLocation.endFloat, 0, -100), + body: SmartRefresher( + enablePullDown: false, + enablePullUp: false, + header: RefreshHeader( + offsetY: top, + ), + controller: _refreshController, + onLoading: _onLoadEvent, + onRefresh: _onRefreshEvent, + child: CustomScrollView( + controller: _controller, + slivers: [ + /// 标题 + SliverAppBar( + title: Text('标题'), + centerTitle: true, + pinned: true, + ), + + /// æœç´¢ + SliverPersistentHeader( + delegate: CustomSliverPersistentHeaderDelegate( + max: 40, + min: 40, + child: Container( + height: double.infinity, + width: double.infinity, + color: Colors.yellowAccent, + ), + ), + // pinned: true, + ), + + /// TAB BAR + SliverPersistentHeader( + delegate: CustomSliverPersistentHeaderDelegate( + max: 40, + min: 40, + child: Container( + height: double.infinity, + width: double.infinity, + color: Colors.redAccent, + child: TabBar( + isScrollable: true, + controller: _tabController, + tabs: List.generate(10, (index) => Text('$index')), + ), + ), + ), + // pinned: true, + ), + + /// 轮播图 + SliverToBoxAdapter( + child: Container( + height: 140, + width: double.infinity, + color: Colors.blueAccent, + ), + ), + + /// 多眼导航 + SliverToBoxAdapter( + child: Container( + height: 70, + width: double.infinity, + color: Colors.yellowAccent, + ), + ), + + /// 商å“列表 + SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return Container( + height: 50, + width: double.infinity, + color: Colors.green[(index % 9 + 1) * 100], + ); + }, childCount: 50)), + ], + ), + ), + ); + } + + /// 悬浮按钮 + Widget _buildFloatWidget() { + return Visibility( + visible: true, + child: GestureDetector( + onTap: ()=> _scrollTop(), + behavior: HitTestBehavior.opaque, + child: Container( + height: 30, + width: 30, + color: Colors.redAccent, + ), + ), + ); + } + + /// 骨架图 + 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: 15, 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), + ), + ), + ) + ], + )); + } +} + +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); + } +} diff --git a/lib/pages/custom_page/custom_page_skeleton.dart b/lib/pages/custom_page/custom_page_skeleton.dart new file mode 100644 index 0000000..9ea8775 --- /dev/null +++ b/lib/pages/custom_page/custom_page_skeleton.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class CommonPageSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/pages/favorites_page/favorites_page.dart b/lib/pages/favorites_page/favorites_page.dart index 448ac4c..1cf4d85 100644 --- a/lib/pages/favorites_page/favorites_page.dart +++ b/lib/pages/favorites_page/favorites_page.dart @@ -268,6 +268,7 @@ class _FavoritesPageContainerState extends State<_FavoritesPageContainer> with T /// 骨架图 Widget _buildSkeletonWidget() { return Scaffold( + backgroundColor: Colors.white, body: Container(), ); } diff --git a/lib/pages/feedback_page/bloc/feedback_bloc.dart b/lib/pages/feedback_page/bloc/feedback_bloc.dart new file mode 100644 index 0000000..8a28a04 --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_bloc.dart @@ -0,0 +1,36 @@ +import 'dart:async'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_repository.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'feedback_event.dart'; +import 'feedback_state.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class FeedbackBloc extends Bloc { + // FeedbackBloc() : super(FeedbackInitial()); + @override + FeedbackState get initialState => FeedbackInitial(); + + FeedBackRepository repository; + + FeedbackBloc(this.repository); + + @override + Stream mapEventToState( + FeedbackEvent event, + ) async* { + /// åˆå§‹åŒ– + if (event is FeedbackInitEvent) { + yield* _mapInitToState(event); + } + } + + /// åˆå§‹åŒ– + Stream _mapInitToState(FeedbackInitEvent event) async* { + var result = await repository.fetchData(event.model); + if (!EmptyUtil.isEmpty(result)) { + yield FeedbackLoadedState(model: result); + } else { + yield FeedbackErrorState(); + } + } +} diff --git a/lib/pages/feedback_page/bloc/feedback_event.dart b/lib/pages/feedback_page/bloc/feedback_event.dart new file mode 100644 index 0000000..c5df86b --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_event.dart @@ -0,0 +1,13 @@ +import 'package:meta/meta.dart'; + +@immutable +abstract class FeedbackEvent {} + +/// åˆå§‹åŒ– +class FeedbackInitEvent extends FeedbackEvent{ + final Map model; + FeedbackInitEvent({this.model}); +} + +/// æäº¤å馈 +class FeedbackSubmitEvent extends FeedbackEvent{} \ No newline at end of file diff --git a/lib/pages/feedback_page/bloc/feedback_repository.dart b/lib/pages/feedback_page/bloc/feedback_repository.dart new file mode 100644 index 0000000..e5578ad --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_repository.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class FeedBackRepository { + /// 获å–ç¼“å­˜æ ·å¼æ•°æ® + + /// 获å–ç½‘ç»œæ ·å¼æ•°æ® + Future fetchData(final Map data) async { + try { + String skip_identifier = !EmptyUtil.isEmpty(data) && data.containsKey('skip_identifier') && !EmptyUtil.isEmpty(data['skip_identifier']) ? data['skip_identifier'] : 'pub.flutter.feedback'; + + var result = await NetUtil.post('/api/v1/mod/$skip_identifier', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + var modListData = result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list'][0]['data']; + if (!EmptyUtil.isEmpty(modListData)) { + FeedbackModel model = FeedbackModel.fromJson(jsonDecode(modListData)); + return model; + } + } + } catch (e, s) { + Logger.error(e, s); + } + return null; + } + + /// 获å–ç½‘ç»œæ•°æ® + +} diff --git a/lib/pages/feedback_page/bloc/feedback_state.dart b/lib/pages/feedback_page/bloc/feedback_state.dart new file mode 100644 index 0000000..5948746 --- /dev/null +++ b/lib/pages/feedback_page/bloc/feedback_state.dart @@ -0,0 +1,15 @@ +import 'package:meta/meta.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_model.dart'; + +@immutable +abstract class FeedbackState {} + +class FeedbackInitial extends FeedbackState {} + +class FeedbackLoadedState extends FeedbackState{ + FeedbackModel model; + + FeedbackLoadedState({this.model}); +} + +class FeedbackErrorState extends FeedbackState{} diff --git a/lib/pages/feedback_page/feedback_page.dart b/lib/pages/feedback_page/feedback_page.dart index f66b956..ddda224 100644 --- a/lib/pages/feedback_page/feedback_page.dart +++ b/lib/pages/feedback_page/feedback_page.dart @@ -5,33 +5,137 @@ import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:image_picker/image_picker.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_bloc.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/bloc/feedback_repository.dart'; import 'package:zhiying_base_widget/pages/feedback_page/feedback_record_page.dart'; +import 'package:zhiying_base_widget/pages/feedback_page/model/feedback_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_image.dart'; import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_tab_widget.dart'; import 'package:zhiying_base_widget/widgets/others/action_selected_alert/action_selected_alert.dart'; import 'package:zhiying_comm/util/log/let_log.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'bloc/feedback_bloc.dart'; +import 'bloc/feedback_repository.dart'; +import 'bloc/feedback_state.dart'; +import 'bloc/feedback_event.dart'; +import 'package:fluttertoast/fluttertoast.dart'; +/// +/// æ„è§å馈 +/// class FeedbackPage extends StatefulWidget { + final Map data; + + FeedbackPage(this.data); + @override _FeedbackPageState createState() => _FeedbackPageState(); } class _FeedbackPageState extends State { + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => FeedbackBloc(FeedBackRepository())..add(FeedbackInitEvent()), + child: _FeedbackPageContainer(), + ); + } +} + +class _FeedbackPageContainer extends StatefulWidget { + @override + __FeedbackPageContainerState createState() => __FeedbackPageContainerState(); +} + +class __FeedbackPageContainerState extends State<_FeedbackPageContainer> { List _images = List(); bool _submitable = false; - TextEditingController _feedback = TextEditingController(); + TextEditingController _feedback; + + TextEditingController _title; + + @override + void initState() { + _feedback = TextEditingController(); + _title = TextEditingController(); + super.initState(); + } @override void dispose() { - _feedback.dispose(); + _feedback?.dispose(); + _title?.dispose(); super.dispose(); } + + /// 选择图片 + void _onAddImage() async { + if (_images.length >= 4) { + Fluttertoast.showToast(msg: '最多上传4张图片'); + return; + } + + var status = await Permission.photos.status; + if (status != PermissionStatus.granted) { + status = await Permission.photos.request(); + } + if (status == PermissionStatus.denied) { + Fluttertoast.showToast(msg: '暂无æƒé™ï¼Œå›¾ç‰‡é€‰æ‹©å¤±è´¥'); + return null; + } + + final picker = ImagePicker(); + PickedFile file; + int index = await showModalBottomSheet( + context: context, + builder: (context) { + return ActionSelectedAlert( + // title: 'æ‹ç…§/选择图片', + actions: ['æ‹ç…§', '从相册选择图片'], + ); + }, + isScrollControlled: false, + backgroundColor: Colors.transparent); + if (index != null) { + if (index == 0) { + file = await picker.getImage(source: ImageSource.camera); + } else { + file = await picker.getImage(source: ImageSource.gallery); + } + + if (file == null) return; + + setState(() { + _images.add(File(file.path)); + }); + + // File resultFile = await EncodeUtil.compressImage(file, 800); + } + } + @override Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) {}, + buildWhen: (prev, current) { + return true; + }, + builder: (context, state) { + if (state is FeedbackLoadedState) { + return _getMainWidget(state?.model); + } + return _getEmptyWidget(); + }, + ); + } + + /// æœ‰æ•°æ® + Widget _getMainWidget(FeedbackModel model) { return Scaffold( resizeToAvoidBottomInset: false, - appBar: _createNav(), + appBar: _createNav(model), body: GestureDetector( onTap: () { FocusScope.of(context).requestFocus(FocusNode()); @@ -45,19 +149,16 @@ class _FeedbackPageState extends State { children: [ Container( width: double.infinity, - margin: EdgeInsets.only( - top: 10, bottom: 10, left: 12.5, right: 12.5), + margin: EdgeInsets.only(top: 10, bottom: 10, left: 12.5, right: 12.5), padding: EdgeInsets.all(12.5), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(7.5)), + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(7.5)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ - _createTitle(), - _createUpload(), - _createSubmit(), + _createTitle(model), + _createUpload(model), + _createSubmit(model), ], ), ) @@ -81,29 +182,31 @@ class _FeedbackPageState extends State { height: 30, margin: EdgeInsets.all(10), decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(15)), + color: HexColor.fromHex(model?.feedbackListBtnBgColor ?? '#FFFFFF'), + borderRadius: BorderRadius.circular(15), + border: Border.all(color: HexColor.fromHex(model?.feedbackListBtnBorderColor ?? '#D1D1D1'), width: 0.5), + ), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: EdgeInsets.only(right: 6), - child: Icon( - Icons.list, - size: 12, + child: CachedNetworkImage( + imageUrl: model?.feedbackListBtnIcon ?? '', + width: 16.5, ), ), Text( - 'å馈记录', - style: - TextStyle(fontSize: 13, color: Color(0xff333333)), + model?.feedbackListBtnText ?? 'å馈记录', + style: TextStyle(fontSize: 13, color: HexColor.fromHex(model?.feedbackListBtnTextColor ?? '#333333'), fontWeight: FontWeight.bold), ) ], ), ), ), ), + const SizedBox(height: 25), ], ), ), @@ -111,8 +214,17 @@ class _FeedbackPageState extends State { ); } - // å¯¼èˆªæ  - Widget _createNav() { + /// æ²¡æ•°æ® + Widget _getEmptyWidget() { + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: _createNav(null), + body: Container(), + ); + } + + /// å¯¼èˆªæ  + Widget _createNav(FeedbackModel mode) { return CupertinoNavigationBar( border: Border( bottom: BorderSide( @@ -120,7 +232,7 @@ class _FeedbackPageState extends State { style: BorderStyle.none, ), ), - backgroundColor: Colors.white, + backgroundColor: HexColor.fromHex(mode?.appBarBgColor ?? '#FFFFFF'), leading: Navigator.canPop(context) ? GestureDetector( child: Container( @@ -138,41 +250,119 @@ class _FeedbackPageState extends State { ) : Container(), middle: Text( - 'æ„è§å馈', + mode?.appBarName ?? 'æ„è§å馈', style: TextStyle( fontSize: 15, - color: Color(0xff333333), + color: HexColor.fromHex(mode?.appBarNameColor ?? '#333333'), ), ), ); } - Widget _createTitle() { + Widget _createTitle(FeedbackModel model) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - 'å馈æè¿°', + model?.feedbackTitle ?? 'å馈æè¿°', style: TextStyle( fontSize: 14, color: Color(0xff333333), fontWeight: FontWeight.bold, ), ), + const SizedBox(height: 2), + /// 异常选项 Padding( padding: EdgeInsets.only(top: 4, bottom: 4), - child: Row( - children: List.generate( - 3, - (index) => Expanded( - child: Container( - margin: EdgeInsets.only(left: 2, right: 2), - height: 28, - child: FeedbackTabWidget('功能异常')), - )), + child: Visibility( + visible: !EmptyUtil.isEmpty(model?.feedbackTypes), + child: Row( + children: model.feedbackTypes + .map((e) => Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: (){ + setState(() { + model.feedbackTypes.forEach((element) { + element.isSelect = false; + }); + e.isSelect = true; + }); + + }, + child: Container( + margin: EdgeInsets.only(left: 2, right: 2), + height: 28, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(2.5), + border: Border.all( + color: HexColor.fromHex(e.isSelect ? model?.feedbackTypeSelectedBorderColor : model?.feedbackTypeNoSelectedBorderColor), + //e.isSelect ? Colors.redAccent : Color(0xffe7e7e7), + width: 1, + ), + ), + child: Stack( + children: [ + Center( + child: Text( + e.name ?? '', + style: TextStyle( + fontSize: 12, + color: HexColor.fromHex(e.isSelect + ? model?.feedbackTypeSelectedTextColor + : model?.feedbackTypeNoSelectedTextColor) //e.isSelect ? Colors.redAccent : Color(0xff999999), + ), + ), + ), + Visibility( + visible: e.isSelect, + child: Align( + alignment: Alignment.bottomRight, + child: CachedNetworkImage( + imageUrl: model?.feedbackTypeSelectedBorderIcon, + width: 10, + ), + ), + ) + ], + ), + )), + ))) + .toList(), + ), + ), + ), + + // 标题 + Container( + height: 32.5, + margin: EdgeInsets.only(left: 2, right: 2, top: 4, bottom: 4), + child: CupertinoTextField( + controller: _title, + textAlignVertical: TextAlignVertical.center, + style: TextStyle(fontSize: 12, color: Color(0xff333333)), + maxLines: 1, + placeholder: model?.feedbackInputHintText ?? '请输入å馈标题(最多15个字)', + placeholderStyle: TextStyle( + fontSize: 12, + color: HexColor.fromHex(model?.feedbackInputHintTextColor ?? '#999999'), + ), + decoration: BoxDecoration(color: Color(0xfff9f9f9), borderRadius: BorderRadius.circular(7.5)), + onChanged: (value) { + if (value == null || value == '' || EmptyUtil.isEmpty(_feedback?.text?.toString()?.trim())) { + _submitable = false; + } else { + _submitable = true; + } + setState(() {}); + }, ), ), + + // 内容 Container( height: 118, margin: EdgeInsets.only(left: 2, right: 2, top: 4, bottom: 4), @@ -181,16 +371,14 @@ class _FeedbackPageState extends State { textAlignVertical: TextAlignVertical.top, style: TextStyle(fontSize: 12, color: Color(0xff333333)), maxLines: null, - placeholder: '请输入您的æ„è§ï¼ˆæœ€å¤š100个字)', + placeholder: model?.feedbackInputContentHintText ?? '请输入您的æ„è§ï¼ˆæœ€å¤š100个字)', placeholderStyle: TextStyle( fontSize: 12, - color: Color(0xff999999), + color: HexColor.fromHex(model?.feedbackInputContentHintTextColor ?? '#999999'), ), - decoration: BoxDecoration( - color: Color(0xfff9f9f9), - borderRadius: BorderRadius.circular(7.5)), + decoration: BoxDecoration(color: Color(0xfff9f9f9), borderRadius: BorderRadius.circular(7.5)), onChanged: (value) { - if (value == null || value == '') { + if (value == null || value == '' || EmptyUtil.isEmpty(_title?.text?.toString()?.trim())) { _submitable = false; } else { _submitable = true; @@ -203,7 +391,7 @@ class _FeedbackPageState extends State { ); } - Widget _createUpload() { + Widget _createUpload(FeedbackModel model) { List images = List(); _images.forEach((file) { images.add(Container( @@ -223,9 +411,7 @@ class _FeedbackPageState extends State { images.add(GestureDetector( child: Container( margin: EdgeInsets.only(top: 4, bottom: 4), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(7.5), - color: Color(0xfff9f9f9)), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(7.5), color: Color(0xfff9f9f9)), height: 80, width: 80, child: Icon( @@ -245,7 +431,7 @@ class _FeedbackPageState extends State { Padding( padding: EdgeInsets.only(right: 10, top: 4, bottom: 4), child: Text( - '上传图片', + model?.feedbackUploadImageTitle ?? '上传图片', style: TextStyle( fontSize: 14, color: Color(0xff333333), @@ -254,7 +440,7 @@ class _FeedbackPageState extends State { ), ), Text( - '最多上传4张,大å°ä¸è¶…过1M/å¼ ', + model?.feedbackUploadImageSubtitle ?? '最多上传4张,大å°ä¸è¶…过1M/å¼ ', style: TextStyle( fontSize: 12, color: Color(0xff999999), @@ -269,27 +455,29 @@ class _FeedbackPageState extends State { ); } - Widget _createSubmit() { + Widget _createSubmit(FeedbackModel model) { return GestureDetector( onTap: () { if (!_submitable) { return; } Logger.debug('æäº¤:${_feedback.text.toString()}'); + Fluttertoast.showToast(msg: 'æäº¤æˆåŠŸï½ž'); + Navigator.pop(context); }, child: Container( margin: EdgeInsets.only(top: 24, bottom: 4), height: 45, decoration: BoxDecoration( - color: _submitable ? Colors.redAccent : Color(0xffffe1e1), + color: HexColor.fromHex(_submitable ? model?.feedbackTypeSelectedBorderColor ?? '' : model?.feedbackPostBtnBgColor ?? ''), borderRadius: BorderRadius.circular(7.5), ), child: Center( child: Text( - 'æäº¤æ„è§', + model?.feedbackPostBtnText ?? 'æäº¤æ„è§', style: TextStyle( fontSize: 14, - color: Colors.white, + color: HexColor.fromHex(model?.feedbackPostBtnTextColor ?? '#FFFFFF'), ), ), ), @@ -297,47 +485,5 @@ class _FeedbackPageState extends State { ); } - void _onAddImage() async { - if (_images.length >= 4) { - Fluttertoast.showToast(msg: '最多上传4张图片'); - return; - } - var status = await Permission.photos.status; - if (status != PermissionStatus.granted) { - status = await Permission.photos.request(); - } - if (status == PermissionStatus.denied) { - Fluttertoast.showToast(msg: '暂无æƒé™ï¼Œå›¾ç‰‡é€‰æ‹©å¤±è´¥'); - return null; - } - - final picker = ImagePicker(); - PickedFile file; - int index = await showModalBottomSheet( - context: context, - builder: (context) { - return ActionSelectedAlert( - // title: 'æ‹ç…§/选择图片', - actions: ['æ‹ç…§', '从相册选择图片'], - ); - }, - isScrollControlled: false, - backgroundColor: Colors.transparent); - if (index != null) { - if (index == 0) { - file = await picker.getImage(source: ImageSource.camera); - } else { - file = await picker.getImage(source: ImageSource.gallery); - } - - if (file == null) return; - - setState(() { - _images.add(File(file.path)); - }); - - // File resultFile = await EncodeUtil.compressImage(file, 800); - } - } } diff --git a/lib/pages/feedback_page/feedback_record_page.dart b/lib/pages/feedback_page/feedback_record_page.dart index 5e307c0..562665c 100644 --- a/lib/pages/feedback_page/feedback_record_page.dart +++ b/lib/pages/feedback_page/feedback_record_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:zhiying_base_widget/pages/feedback_page/widgets/feedback_record_item.dart'; +import 'package:zhiying_base_widget/widgets/empty/empty_widget.dart'; class FeedbackRecordPage extends StatefulWidget { @override @@ -17,11 +18,12 @@ class _FeedbackRecordPageState extends State { FocusScope.of(context).requestFocus(FocusNode()); }, child: SafeArea( - child: ListView.builder( - itemCount: 10, - itemBuilder: (context, index) { - return FeedbackRecordItem(); - }), + child: Center(child: EmptyWidget()), + // child: ListView.builder( + // itemCount: 10, + // itemBuilder: (context, index) { + // return FeedbackRecordItem(); + // }), ), ), ); diff --git a/lib/pages/feedback_page/model/feedback_model.dart b/lib/pages/feedback_page/model/feedback_model.dart new file mode 100644 index 0000000..ccf25be --- /dev/null +++ b/lib/pages/feedback_page/model/feedback_model.dart @@ -0,0 +1,194 @@ +class FeedbackModel { + String appBarName; + String appBarNameColor; + String appBarBgColor; + String bgColor; + String feedbackBgColor; + String feedbackTitle; + String feedbackTypeNoSelectedBorderColor; + String feedbackTypeNoSelectedTextColor; + String feedbackTypeSelectedBorderColor; + String feedbackTypeSelectedTextColor; + String feedbackTypeSelectedBorderIcon; + List feedbackTypes; + String feedbackInputBgColor; + String feedbackInputHintText; + String feedbackInputHintTextColor; + String feedbackInputLimitText; + String feedbackInputLimitTextColor; + String feedbackInputContentBgColor; + String feedbackInputContentHintText; + String feedbackInputContentLimitText; + String feedbackInputContentHintTextColor; + String feedbackInputContentLimitTextColor; + String feedbackUploadImageTitle; + String feedbackUploadImageSubtitle; + String feedbackPostBtnText; + String feedbackPostBtnBgColor; + String feedbackPostBtnTextColor; + String feedbackListBtnIcon; + String feedbackListBtnText; + String feedbackListBtnTextColor; + String feedbackListBtnBgColor; + String feedbackListBtnBorderColor; + String requiredLogin; + String requiredTaobaoAuth; + String skipIdentifier; + + FeedbackModel( + {this.appBarName, + this.appBarNameColor, + this.appBarBgColor, + this.bgColor, + this.feedbackBgColor, + this.feedbackTitle, + this.feedbackTypeNoSelectedBorderColor, + this.feedbackTypeNoSelectedTextColor, + this.feedbackTypeSelectedBorderColor, + this.feedbackTypeSelectedTextColor, + this.feedbackTypeSelectedBorderIcon, + this.feedbackTypes, + this.feedbackInputBgColor, + this.feedbackInputHintText, + this.feedbackInputHintTextColor, + this.feedbackInputLimitText, + this.feedbackInputLimitTextColor, + this.feedbackInputContentBgColor, + this.feedbackInputContentHintText, + this.feedbackInputContentLimitText, + this.feedbackInputContentHintTextColor, + this.feedbackInputContentLimitTextColor, + this.feedbackUploadImageTitle, + this.feedbackUploadImageSubtitle, + this.feedbackPostBtnText, + this.feedbackPostBtnBgColor, + this.feedbackPostBtnTextColor, + this.feedbackListBtnIcon, + this.feedbackListBtnText, + this.feedbackListBtnTextColor, + this.feedbackListBtnBgColor, + this.feedbackListBtnBorderColor, + this.requiredLogin, + this.requiredTaobaoAuth, + this.skipIdentifier}); + + FeedbackModel.fromJson(Map json) { + appBarName = json['app_bar_name']; + appBarNameColor = json['app_bar_name_color']; + appBarBgColor = json['app_bar_bg_color']; + bgColor = json['bg_color']; + feedbackBgColor = json['feedback_bg_color']; + feedbackTitle = json['feedback_title']; + feedbackTypeNoSelectedBorderColor = + json['feedback_type_no_selected_border_color']; + feedbackTypeNoSelectedTextColor = + json['feedback_type_no_selected_text_color']; + feedbackTypeSelectedBorderColor = + json['feedback_type_selected_border_color']; + feedbackTypeSelectedTextColor = json['feedback_type_selected_text_color']; + feedbackTypeSelectedBorderIcon = json['feedback_type_selected_border_icon']; + if (json['feedback_types'] != null) { + feedbackTypes = new List(); + json['feedback_types'].forEach((v) { + feedbackTypes.add(new FeedbackTypes.fromJson(v)); + }); + } + feedbackInputBgColor = json['feedback_input_bg_color']; + feedbackInputHintText = json['feedback_input_hint_text']; + feedbackInputHintTextColor = json['feedback_input_hint_text_color']; + feedbackInputLimitText = json['feedback_input_limit_text']; + feedbackInputLimitTextColor = json['feedback_input_limit_text_color']; + feedbackInputContentBgColor = json['feedback_input_content_bg_color']; + feedbackInputContentHintText = json['feedback_input_content_hint_text']; + feedbackInputContentLimitText = json['feedback_input_content_limit_text']; + feedbackInputContentHintTextColor = + json['feedback_input_content_hint_text_color']; + feedbackInputContentLimitTextColor = + json['feedback_input_content_limit_text_color']; + feedbackUploadImageTitle = json['feedback_upload_image_title']; + feedbackUploadImageSubtitle = json['feedback_upload_image_subtitle']; + feedbackPostBtnText = json['feedback_post_btn_text']; + feedbackPostBtnBgColor = json['feedback_post_btn_bg_color']; + feedbackPostBtnTextColor = json['feedback_post_btn_text_color']; + feedbackListBtnIcon = json['feedback_list_btn_icon']; + feedbackListBtnText = json['feedback_list_btn_text']; + feedbackListBtnTextColor = json['feedback_list_btn_text_color']; + feedbackListBtnBgColor = json['feedback_list_btn_bg_color']; + feedbackListBtnBorderColor = json['feedback_list_btn_border_color']; + requiredLogin = json['required_login']; + requiredTaobaoAuth = json['required_taobao_auth']; + skipIdentifier = json['skip_identifier']; + } + + Map toJson() { + final Map data = new Map(); + data['app_bar_name'] = this.appBarName; + data['app_bar_name_color'] = this.appBarNameColor; + data['app_bar_bg_color'] = this.appBarBgColor; + data['bg_color'] = this.bgColor; + data['feedback_bg_color'] = this.feedbackBgColor; + data['feedback_title'] = this.feedbackTitle; + data['feedback_type_no_selected_border_color'] = + this.feedbackTypeNoSelectedBorderColor; + data['feedback_type_no_selected_text_color'] = + this.feedbackTypeNoSelectedTextColor; + data['feedback_type_selected_border_color'] = + this.feedbackTypeSelectedBorderColor; + data['feedback_type_selected_text_color'] = + this.feedbackTypeSelectedTextColor; + data['feedback_type_selected_border_icon'] = + this.feedbackTypeSelectedBorderIcon; + if (this.feedbackTypes != null) { + data['feedback_types'] = + this.feedbackTypes.map((v) => v.toJson()).toList(); + } + data['feedback_input_bg_color'] = this.feedbackInputBgColor; + data['feedback_input_hint_text'] = this.feedbackInputHintText; + data['feedback_input_hint_text_color'] = this.feedbackInputHintTextColor; + data['feedback_input_limit_text'] = this.feedbackInputLimitText; + data['feedback_input_limit_text_color'] = this.feedbackInputLimitTextColor; + data['feedback_input_content_bg_color'] = this.feedbackInputContentBgColor; + data['feedback_input_content_hint_text'] = + this.feedbackInputContentHintText; + data['feedback_input_content_limit_text'] = + this.feedbackInputContentLimitText; + data['feedback_input_content_hint_text_color'] = + this.feedbackInputContentHintTextColor; + data['feedback_input_content_limit_text_color'] = + this.feedbackInputContentLimitTextColor; + data['feedback_upload_image_title'] = this.feedbackUploadImageTitle; + data['feedback_upload_image_subtitle'] = this.feedbackUploadImageSubtitle; + data['feedback_post_btn_text'] = this.feedbackPostBtnText; + data['feedback_post_btn_bg_color'] = this.feedbackPostBtnBgColor; + data['feedback_post_btn_text_color'] = this.feedbackPostBtnTextColor; + data['feedback_list_btn_icon'] = this.feedbackListBtnIcon; + data['feedback_list_btn_text'] = this.feedbackListBtnText; + data['feedback_list_btn_text_color'] = this.feedbackListBtnTextColor; + data['feedback_list_btn_bg_color'] = this.feedbackListBtnBgColor; + data['feedback_list_btn_border_color'] = this.feedbackListBtnBorderColor; + data['required_login'] = this.requiredLogin; + data['required_taobao_auth'] = this.requiredTaobaoAuth; + data['skip_identifier'] = this.skipIdentifier; + return data; + } +} + +class FeedbackTypes { + String typeId; + String name; + bool isSelect = false; + + FeedbackTypes({this.typeId, this.name}); + + FeedbackTypes.fromJson(Map json) { + typeId = json['type_id']; + name = json['name']; + } + + Map toJson() { + final Map data = new Map(); + data['type_id'] = this.typeId; + data['name'] = this.name; + return data; + } +} diff --git a/lib/pages/goods_details_page/bloc/goods_details_page_repository.dart b/lib/pages/goods_details_page/bloc/goods_details_page_repository.dart index 11c8e7f..c31cec0 100644 --- a/lib/pages/goods_details_page/bloc/goods_details_page_repository.dart +++ b/lib/pages/goods_details_page/bloc/goods_details_page_repository.dart @@ -13,7 +13,8 @@ class GoodsDetailsPageRepository { String goodId = model['good_id']?.toString(); if (!EmptyUtil.isEmpty(provider) && !EmptyUtil.isEmpty(goodId)) { Map detailData = model['detail_data']; - return _baseDataProcess(true, goodId, provider, detailData); + String good_image = model['good_image']; + return _baseDataProcess(true, goodId, provider, detailData, good_image); } } } catch (e, s) { @@ -22,7 +23,7 @@ class GoodsDetailsPageRepository { return null; } - /// èŽ·å–æ•°æ® + /// 获å–ç½‘ç»œæ•°æ® Future>> fetchInitModData(final Map model) async { try { String provider = model['provider']; @@ -31,7 +32,7 @@ class GoodsDetailsPageRepository { Logger.log('商å“类型 = $provider, 商å“ID = $goodId'); var result = await NetUtil.post('/api/v1/detail/$provider/$goodId', method: NetMethod.GET); if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { - return _baseDataProcess(false, goodId, provider, result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); + return _baseDataProcess(false, goodId, provider, result[GlobalConfig.HTTP_RESPONSE_KEY_DATA], null); } } } catch (e, s) { @@ -41,7 +42,7 @@ class GoodsDetailsPageRepository { } /// æ•°æ®å¤„ç† - List> _baseDataProcess(bool parentInput,String goodId, String provider, final Map model) { + List> _baseDataProcess(bool parentInput,String goodId, String provider, final Map model, String coverImage) { if (!EmptyUtil.isEmpty(provider) && !EmptyUtil.isEmpty(goodId) && !EmptyUtil.isEmpty(model)) { try { List> _pageData = []; @@ -59,6 +60,9 @@ class GoodsDetailsPageRepository { var imageList = data['image_list']; if (!EmptyUtil.isEmpty(imageList)) { _parentGoodsCoverImg = imageList[0]; + }else if (!EmptyUtil.isEmpty(coverImage)){ + _parentGoodsCoverImg = coverImage; + data['image_list'] = [coverImage]; } } catch (e, s) { Logger.error(e, s); @@ -69,6 +73,8 @@ class GoodsDetailsPageRepository { if(data['image_list'][0] != _parentGoodsCoverImg) { data['image_list'].insert(0, _parentGoodsCoverImg); } + }else if(!EmptyUtil.isEmpty(_parentGoodsCoverImg) && !EmptyUtil.isEmpty(data) && EmptyUtil.isEmpty(data['image_list'])){ + data['image_list'] = [_parentGoodsCoverImg]; } }catch(e, s){ Logger.error(e, s); diff --git a/lib/pages/search_page/bloc/search_bloc.dart b/lib/pages/search_page/bloc/search_bloc.dart index 160c828..17cf01f 100644 --- a/lib/pages/search_page/bloc/search_bloc.dart +++ b/lib/pages/search_page/bloc/search_bloc.dart @@ -28,11 +28,6 @@ class SearchBloc extends Bloc { if (event is SearchInitEvent) { yield* _mapInitEventToState(event); } - - /// æœç´¢çš„æ–¹æ³• - if (event is SearchSubmitEvent) { - yield* _mapSearchSubmitToState(event); - } } /// åˆå§‹åŒ– @@ -53,8 +48,5 @@ class SearchBloc extends Bloc { } } - /// æœç´¢çš„æ–¹æ³• - Stream _mapSearchSubmitToState(SearchSubmitEvent event) async* { - var result = await repository.fetchSearchSubmit(event.model); - } + } diff --git a/lib/pages/search_page/bloc/search_repository.dart b/lib/pages/search_page/bloc/search_repository.dart index 6f78df2..3a9926d 100644 --- a/lib/pages/search_page/bloc/search_repository.dart +++ b/lib/pages/search_page/bloc/search_repository.dart @@ -4,32 +4,39 @@ class SearchRepository { List> _pageData = []; /// 获å–ç½‘ç»œæ•°æ® - Future>> fetchInit(final Map model) async { - var result = await NetUtil.post('/api/v1/mod/pub.flutter.search_index', method: NetMethod.GET, cache: true); - if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { - try { + Future>> fetchInit(final Map data) async { + try { + String skipIdentifier = !EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) + ? data[GlobalConfig.SKIP_IDENTIFIER] + : 'pub.flutter.search_index'; + + var result = await NetUtil.post('/api/v1/mod/$skipIdentifier', method: NetMethod.GET, cache: true); + if (NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result)) { _pageData = List.from(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]['mod_list']); return _pageData; - } catch (e) { - Logger.error(e.toString()); } + } catch (e, s) { + Logger.error(e.toString(), s); } return null; } /// 获å–ç¼“å­˜æ•°æ® - Future>> fetchCachedData(final Map model) async{ - var result = await NetUtil.getRequestCachedData('/api/v1/mod/pub.flutter.search_index'); + Future>> fetchCachedData(final Map data) async { try { + String skipIdentifier = !EmptyUtil.isEmpty(data) && data.containsKey(GlobalConfig.SKIP_IDENTIFIER) && !EmptyUtil.isEmpty(data[GlobalConfig.SKIP_IDENTIFIER]) + ? data[GlobalConfig.SKIP_IDENTIFIER] + : 'pub.flutter.search_index'; + + var result = await NetUtil.getRequestCachedData('/api/v1/mod/$skipIdentifier'); if (!EmptyUtil.isEmpty(result)) { return List.from(result['mod_list']); } - }catch(e){} + } catch (e, s) { + Logger.error(e, s); + } return null; } - /// æœç´¢çš„æ–¹æ³• - Future fetchSearchSubmit(final String model) async { - var result = await NetUtil.post(''); - } + } diff --git a/lib/pages/search_page/search_page.dart b/lib/pages/search_page/search_page.dart index b140ef1..79cb359 100644 --- a/lib/pages/search_page/search_page.dart +++ b/lib/pages/search_page/search_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_repository.dart'; @@ -115,12 +116,15 @@ class _SearchPageContianerState extends State { /// 主视图 Widget _getMainWidget(List> datas) { - return Scaffold( - backgroundColor: Colors.white, - body: Listener( - onPointerDown: (down) => RouterUtil.hideKeyboard(context), - child: Column( - children: _createContentWidget(datas), + return AnnotatedRegion( + value: SystemUiOverlayStyle.dark, + child: Scaffold( + backgroundColor: Colors.white, + body: Listener( + onPointerDown: (down) => RouterUtil.hideKeyboard(context), + child: Column( + children: _createContentWidget(datas), + ), ), ), ); diff --git a/lib/pages/search_result_page/search_result_page.dart b/lib/pages/search_result_page/search_result_page.dart index e5c6dce..32a493c 100644 --- a/lib/pages/search_result_page/search_result_page.dart +++ b/lib/pages/search_result_page/search_result_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:zhiying_base_widget/pages/search_page/notifier/search_tag_notifier.dart'; import 'package:zhiying_base_widget/pages/search_result_page/bloc/search_result_bloc.dart'; import 'package:zhiying_base_widget/pages/search_think_page/bloc/search_think_bloc.dart'; @@ -82,12 +83,15 @@ class _SearchResultContianerState extends State { /// 获å–主视图 Widget _getMainWidget(List> data) { - return Scaffold( - backgroundColor: Colors.white, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: _createContentWidget(data), + return AnnotatedRegion( + value: SystemUiOverlayStyle.dark, + child: Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: _createContentWidget(data), + ), ), ); } diff --git a/lib/pages/setting_page/setting_page.dart b/lib/pages/setting_page/setting_page.dart index 03236c8..09f30df 100644 --- a/lib/pages/setting_page/setting_page.dart +++ b/lib/pages/setting_page/setting_page.dart @@ -185,9 +185,10 @@ class _SettingContainerState extends State<_SettingContainer> { ), ), ), - onTap: () { + onTap: () async{ Logger.debug('退出登录'); - Provider.of(context, listen: false).unLogin(); + await Provider.of(context, listen: false).unLogin(); + Navigator.maybePop(context); }, ); } diff --git a/lib/register.dart b/lib/register.dart index 7ca9d0b..d44bd3e 100644 --- a/lib/register.dart +++ b/lib/register.dart @@ -1,7 +1,9 @@ +import 'package:flutter/cupertino.dart'; import 'package:sharesdk_plugin/sharesdk_interface.dart'; import 'package:sharesdk_plugin/sharesdk_register.dart'; import 'package:zhiying_base_widget/pages/bil_detail_page/bil_detail_page.dart'; import 'package:zhiying_base_widget/pages/about_us_page/about_us_page.dart'; +import 'package:zhiying_base_widget/pages/custom_page/custom_page.dart'; import 'package:zhiying_base_widget/pages/feedback_page/feedback_page.dart'; import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; @@ -99,7 +101,7 @@ class BaseWidgetRegister { }); Application.addMethod(() async { - return Future.delayed(Duration(seconds: 2)); + return Future.delayed(Duration(seconds: 1)); }); } @@ -112,11 +114,11 @@ class BaseWidgetRegister { PageFactory.regist('pub.flutter.hot_rank', (model) => HotRankingPage(model)); PageFactory.regist('pub.flutter.my_wallet', (model) => WalletPage(data: model)); PageFactory.regist('goods_details', (model) => GoodsDetailsPage(model)); - PageFactory.regist('search', (model) => SearchPage(model)); + PageFactory.regist('pub.flutter.search_index', (model) => SearchPage(model)); PageFactory.regist('search_item_page', (model) => SearchItemPage(model)); PageFactory.regist('pub.flutter.search_index', (model) => SearchResultPage(model)); PageFactory.regist('search_result_item', (model) => SearchResultItemPage(model)); - PageFactory.regist('pub.flutter.feedback', (model) => FeedbackPage()); + PageFactory.regist('pub.flutter.feedback', (model) => FeedbackPage(model)); PageFactory.regist('pub.flutter.wechat_teacher', (model) => WechatTeacherPage()); PageFactory.regist('pub.flutter.cash_out', (model) => WithdrawPage(model)); @@ -159,6 +161,8 @@ class BaseWidgetRegister { PageFactory.regist('pub.flutter.message_settings', (model) => MessageSettingsPage(model)); /// 钱包明细 PageFactory.regist('pub.flutter.my_wallet_detail', (model) => BilDetailPage(model)); + /// é€šç”¨æ¨¡å— + PageFactory.regist('pub.flutter.custom', (model) => CustomPage(model)); } // 注册控件 diff --git a/lib/widgets/custom/appbar/custom_appbar_creater.dart b/lib/widgets/custom/appbar/custom_appbar_creater.dart new file mode 100644 index 0000000..573d025 --- /dev/null +++ b/lib/widgets/custom/appbar/custom_appbar_creater.dart @@ -0,0 +1,16 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter/material.dart'; + +import 'custom_appbar_widget.dart'; + +class CustomAppBarCreater extends WidgetCreater { + @override + List createWidgets(Map model) { + return [CustomAppBarWidget(model)]; + } + + @override + bool isSliverChild() { + return true; + } +} diff --git a/lib/widgets/custom/appbar/custom_appbar_widget.dart b/lib/widgets/custom/appbar/custom_appbar_widget.dart new file mode 100644 index 0000000..a18ed02 --- /dev/null +++ b/lib/widgets/custom/appbar/custom_appbar_widget.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 通用模å—çš„AppBar Widget +/// +class CustomAppBarWidget extends StatelessWidget { + final Map data; + Map model; + + CustomAppBarWidget(this.data, {Key key}) : super(key: key) { + try { + var dataItem = data['data']; + if (!EmptyUtil.isEmpty(dataItem)) { + model = dataItem is Map ? dataItem : dataItem is String ? jsonDecode(dataItem) : null; + } + } catch (e, s) { + Logger.error(e, s); + } + } + + @override + Widget build(BuildContext context) { + return SliverAppBar( + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 22, + color: HexColor.fromHex( '#333333'), + ), + onPressed: () => Navigator.maybePop(context), + ), + title: Text( + '自定义页é¢', + style: TextStyle( + fontSize: 15, + color: HexColor.fromHex('#333333'), + fontWeight: FontWeight.bold, + ), + ), + ); + } +} diff --git a/lib/widgets/custom/search/custom_search_widget.dart b/lib/widgets/custom/search/custom_search_widget.dart new file mode 100644 index 0000000..f39dc3d --- /dev/null +++ b/lib/widgets/custom/search/custom_search_widget.dart @@ -0,0 +1,13 @@ + +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 通用模å—çš„æœç´¢æ  +/// +class CustomSearchWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/widgets/custom/tabbar/custom_tabbar_widget.dart b/lib/widgets/custom/tabbar/custom_tabbar_widget.dart new file mode 100644 index 0000000..4bda984 --- /dev/null +++ b/lib/widgets/custom/tabbar/custom_tabbar_widget.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + + +/// +/// 通用模å—çš„tabbar +/// +class CustomTabBarWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/widgets/goods_details/coupon/counpon_widget.dart b/lib/widgets/goods_details/coupon/counpon_widget.dart index e76d216..cfd6107 100644 --- a/lib/widgets/goods_details/coupon/counpon_widget.dart +++ b/lib/widgets/goods_details/coupon/counpon_widget.dart @@ -9,9 +9,7 @@ import 'package:zhiying_comm/zhiying_comm.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:provider/provider.dart'; -import 'package:flutter_alibc/alibc_model.dart'; -import 'package:flutter_alibc/flutter_alibc.dart'; -import 'package:fluttertoast/fluttertoast.dart'; +import 'package:zhiying_comm/util/turn_chain/turn_chain_util.dart'; /// /// 优惠券widget @@ -60,33 +58,7 @@ class _CounponWidgetContainerState extends State { /// ç‚¹å‡»é¢†å– void _onJump(CounponModel model) async{ - print(_user?.toString()); - if (_user?.token == null || _user.token == '') { - print('need login...'); - RouterUtil.goLogin(context); - return; - } - - if (EmptyUtil.isEmpty(model.buy_url)) { - Fluttertoast.showToast(msg: '购买链接ä¸å­˜åœ¨'); - return; - } - if (model.provider== 'taobao') { - // bool isAuth = await TaobaoAuth.isAuth(); - if (!_user.isTBAuth) { - TaobaoAuth.auth(context); - return; - } - TradeResult result; - if (Platform.isAndroid) { - result = await FlutterAlibc.openByUrl(url: model.buy_url, backUrl: "alisdk://"); - } else if (Platform.isIOS) { - result = await FlutterAlibc.openByUrl(url: model.buy_url); - } - Logger.debug('${result.errorCode} ${result.errorMessage} '); - } else { - RouterUtil.openWebview(model.buy_url, context); - } + TurnChainUtil.openReceiveCoupon(context, _user, model.provider, model.toJson()); } @override diff --git a/lib/widgets/goods_details/coupon/model/counpon_model.dart b/lib/widgets/goods_details/coupon/model/counpon_model.dart index 9656c9f..3f3b0c9 100644 --- a/lib/widgets/goods_details/coupon/model/counpon_model.dart +++ b/lib/widgets/goods_details/coupon/model/counpon_model.dart @@ -12,6 +12,7 @@ class CounponModel { String buy_url; String provider; String goood_id; + ConvertArgs convertArgs; CounponModel({ this.goood_id, @@ -27,6 +28,7 @@ class CounponModel { this.price_type, this.price_type_color, this.buy_url, + this.convertArgs, }); factory CounponModel.fromJson(Map json) { @@ -44,6 +46,8 @@ class CounponModel { price_type: json['price_type'], price_type_color: json['price_type_color'], buy_url: json['buy_url'], + convertArgs: json['convert_args'] != null ? ConvertArgs.fromJson(json['convert_args']) : null, + ); } @@ -62,6 +66,42 @@ class CounponModel { data['buy_url'] = this.buy_url; data['provider'] = this.provider; data['goood_id'] = this.goood_id; + if (this.convertArgs != null) { + data['convert_args'] = this.convertArgs.toJson(); + } + return data; } } + +class ConvertArgs { + String gid; + String goodUrl; + String couponUrl; + String couponPrice; + String activityUrl; + String isShare; + + ConvertArgs({this.gid, this.goodUrl, this.couponUrl, this.couponPrice, this.activityUrl, this.isShare}); + + ConvertArgs.fromJson(Map json) { + gid = json['gid']; + goodUrl = json['good_url']; + couponUrl = json['coupon_url']; + couponPrice = json['coupon_price']; + activityUrl = json['activity_url']; + isShare = json['is_share']; + } + + Map toJson() { + final Map data = new Map(); + data['gid'] = this.gid; + data['good_url'] = this.goodUrl; + data['coupon_url'] = this.couponUrl; + data['coupon_price'] = this.couponPrice; + data['activity_url'] = this.activityUrl; + data['is_share'] = this.isShare; + return data; + } +} + diff --git a/lib/widgets/goods_details/footer/goods_details_footer_widget.dart b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart index aeda916..44632b1 100644 --- a/lib/widgets/goods_details/footer/goods_details_footer_widget.dart +++ b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart @@ -1,11 +1,8 @@ import 'dart:convert'; -import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_alibc/alibc_model.dart'; -import 'package:flutter_alibc/flutter_alibc.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:provider/provider.dart'; @@ -15,6 +12,7 @@ import 'package:zhiying_base_widget/widgets/goods_details/footer/bloc/goods_deta import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_sk.dart'; import 'package:zhiying_base_widget/widgets/goods_details/footer/model/goods_details_footer_model.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:zhiying_comm/util/turn_chain/turn_chain_util.dart'; /// /// 商å“详情底部Widget @@ -30,8 +28,7 @@ class GoodsDetailsFooterWidget extends StatelessWidget { visible: !EmptyUtil.isEmpty(model), replacement: GoodsDetailsFooterSkeleton(), child: BlocProvider( - create: (_) => - GoodsDetailsFooterBloc(repository: GoodsDetailsFooterRepository()), + create: (_) => GoodsDetailsFooterBloc(repository: GoodsDetailsFooterRepository()), //..add(GoodsDetailsFooterInitEvent(model: model)), child: GooddsDetailsFooterContainer( model, @@ -48,18 +45,15 @@ class GooddsDetailsFooterContainer extends StatefulWidget { const GooddsDetailsFooterContainer(this.model, {Key key}) : super(key: key); @override - _GooddsDetailsFooterContainerState createState() => - _GooddsDetailsFooterContainerState(); + _GooddsDetailsFooterContainerState createState() => _GooddsDetailsFooterContainerState(); } -class _GooddsDetailsFooterContainerState - extends State { +class _GooddsDetailsFooterContainerState extends State { UserInfoModel _user; @override void initState() { - BlocProvider.of(context) - .add(GoodsDetailsFooterInitEvent(model: widget?.model)); + BlocProvider.of(context).add(GoodsDetailsFooterInitEvent(model: widget?.model)); super.initState(); } @@ -73,9 +67,7 @@ class _GooddsDetailsFooterContainerState void _openHome() { Navigator.pushAndRemoveUntil( context, - CupertinoPageRoute( - builder: (BuildContext context) => - PageFactory.create('homePage', null)), + CupertinoPageRoute(builder: (BuildContext context) => PageFactory.create('homePage', null)), (Route route) => false, ); } @@ -83,84 +75,57 @@ class _GooddsDetailsFooterContainerState /// æ”¶è— void _collectOnClick(GoodsDetailsFooterModel model) { bool isCollect = model.isFav == '0'; - if(isCollect){ // æ”¶è— + if (isCollect) { + // æ”¶è— BlocProvider.of(context).add(GoodsDetailsFooterCollectEvent(model: model?.favArgs?.toJson())); - }else{ // å–æ¶ˆæ”¶è— + } else { + // å–æ¶ˆæ”¶è— BlocProvider.of(context).add(GoodsDetailsFooterDeleteCollectEvent(model: model?.favArgs?.toJson())); } } /// 分享 void _shareOnClick(GoodsDetailsFooterModel model) async { - print(_user?.toString()); - if (_user?.token == null || _user.token == '') { - print('need login...'); - RouterUtil.goLogin(context); - return; - } - - String d = widget.model['data']; - Map data = jsonDecode(d); - - Map shareArgs = data['share_url_args'] ?? {}; - String shopType = shareArgs['type']; + // print(_user?.toString()); + // if (_user?.token == null || _user.token == '') { + // print('need login...'); + // RouterUtil.goLogin(context); + // return; + // } + // + // String d = widget.model['data']; + // Map data = jsonDecode(d); + // + // Map shareArgs = data['share_url_args'] ?? {}; + // String shopType = shareArgs['type']; + // + // if (data.containsKey('buy_url')) { + // String url = data['buy_url']; + // if (url == null || url == '') { + // Fluttertoast.showToast(msg: '购买链接ä¸å­˜åœ¨'); + // return; + // } + // if (shopType == 'taobao') { + // // bool isAuth = await TaobaoAuth.isAuth(); + // if (!_user.isTBAuth) { + // TaobaoAuth.auth(context); + // return; + // } + // } + // Navigator.of(context).push(CupertinoPageRoute( + // builder: (context) => GoodsSharePage(widget.model))); + // } - if (data.containsKey('buy_url')) { - String url = data['buy_url']; - if (url == null || url == '') { - Fluttertoast.showToast(msg: '购买链接ä¸å­˜åœ¨'); - return; - } - if (shopType == 'taobao') { - // bool isAuth = await TaobaoAuth.isAuth(); - if (!_user.isTBAuth) { - TaobaoAuth.auth(context); - return; - } - } - Navigator.of(context).push(CupertinoPageRoute( - builder: (context) => GoodsSharePage(widget.model))); + Map result = await TurnChainUtil.getShareTurnChain(context, _user, model.provider, model.convertArgs.toJson()); + if (!EmptyUtil.isEmpty(result)) { + model.shareUrlArgs.buyUrl = result['open_app_url']; + Navigator.of(context).push(CupertinoPageRoute(builder: (context) => GoodsSharePage(model.toJson()))); } } /// è‡ªè´­çœ - void _savemoneyOnClick(GoodsDetailsFooterModel model) async { - print(_user?.toString()); - if (_user?.token == null || _user.token == '') { - print('need login...'); - RouterUtil.goLogin(context); - return; - } - - String d = widget.model['data']; - Map data = jsonDecode(d); - - Map shareArgs = data['share_url_args'] ?? {}; - String shopType = shareArgs['type']; - - if (data.containsKey('buy_url')) { - String url = data['buy_url']; - if (url == null || url == '') { - Fluttertoast.showToast(msg: '购买链接ä¸å­˜åœ¨'); - return; - } - if (shopType == 'taobao') { - // bool isAuth = await TaobaoAuth.isAuth(); - if (!_user.isTBAuth) { - TaobaoAuth.auth(context); - return; - } - TradeResult result; - if (Platform.isAndroid) { - result = await FlutterAlibc.openByUrl(url: url, backUrl: "alisdk://"); - } else if (Platform.isIOS) { - result = await FlutterAlibc.openByUrl(url: url); - } - Logger.debug('${result.errorCode} ${result.errorMessage} '); - } else { - RouterUtil.openWebview(url, context); - } - } + void _saveMoneyOnClick(GoodsDetailsFooterModel model) async { + await TurnChainUtil.openReceiveCoupon(context, _user, model?.provider, model?.convertArgs?.toJson()); } @override @@ -188,8 +153,7 @@ class _GooddsDetailsFooterContainerState return SafeArea( child: Container( width: double.infinity, - padding: EdgeInsets.only( - bottom: (height > 10 ? 0 : 8), top: 8, left: 21, right: 12.5), + padding: EdgeInsets.only(bottom: (height > 10 ? 0 : 8), top: 8, left: 21, right: 12.5), decoration: BoxDecoration( // boxShadow: [ // BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0), blurRadius: 5.0, spreadRadius: 2.0), @@ -228,18 +192,14 @@ class _GooddsDetailsFooterContainerState onTap: () => _openHome(), child: Padding( padding: const EdgeInsets.only(right: 35), - child: _getCustomWidget(model?.home ?? '首页', - model?.home_color ?? '999999', model?.home_icon ?? ''), + child: _getCustomWidget(model?.home ?? '首页', model?.home_color ?? '999999', model?.home_icon ?? ''), )), GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => _collectOnClick(model), child: Padding( padding: const EdgeInsets.only(right: 0), - child: _getCustomWidget( - model?.collect ?? 'æ”¶è—', - model?.collect_color ?? '999999', - model?.isFav == '0' ? model?.collect_icon ?? '' : model?.collected_icon ?? ''))) + child: _getCustomWidget(model?.collect ?? 'æ”¶è—', model?.collect_color ?? '999999', model?.isFav == '0' ? model?.collect_icon ?? '' : model?.collected_icon ?? ''))) ], ); } @@ -266,40 +226,30 @@ class _GooddsDetailsFooterContainerState width: 110, // padding: const EdgeInsets.only(left: 30, right: 30, top: 5, bottom: 5), decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - HexColor.fromHex(model?.share_earn_bg1_color ?? '#FFCA66'), - HexColor.fromHex(model?.share_earn_bg2_color ?? '#FFD961') - ], begin: Alignment.centerLeft, end: Alignment.centerRight), - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(25), topLeft: Radius.circular(25))), + gradient: LinearGradient( + colors: [HexColor.fromHex(model?.share_earn_bg1_color ?? '#FFCA66'), HexColor.fromHex(model?.share_earn_bg2_color ?? '#FFD961')], + begin: Alignment.centerLeft, + end: Alignment.centerRight), + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(25), topLeft: Radius.circular(25))), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ RichText( - text: TextSpan( - text: 'Â¥ ', - style: TextStyle( - fontSize: 12, - color: HexColor.fromHex( - model?.share_earn_val_color ?? 'FFFFFF')), - children: [ - TextSpan( - text: model?.share_value ?? '0.0', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: HexColor.fromHex( - model?.share_earn_color ?? '#FFFFFF'), - fontFamily: 'Din', - package: 'zhiying_base_widget')), - ]), + text: TextSpan(text: 'Â¥ ', style: TextStyle(fontSize: 12, color: HexColor.fromHex(model?.share_earn_val_color ?? 'FFFFFF')), children: [ + TextSpan( + text: model?.share_value ?? '0.0', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: HexColor.fromHex(model?.share_earn_color ?? '#FFFFFF'), + fontFamily: 'Din', + package: 'zhiying_base_widget')), + ]), ), Text( model?.share_earn ?? '分享赚', - style: TextStyle( - color: HexColor.fromHex(model?.share_earn_color ?? '#FFFFFF'), - fontSize: 12), + style: TextStyle(color: HexColor.fromHex(model?.share_earn_color ?? '#FFFFFF'), fontSize: 12), ), ], ), @@ -311,49 +261,36 @@ class _GooddsDetailsFooterContainerState Widget _getZgsButton(GoodsDetailsFooterModel model) { return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: () => _savemoneyOnClick(model), + onTap: () => _saveMoneyOnClick(model), child: Container( alignment: Alignment.center, // padding: const EdgeInsets.only(left: 30, right: 30, top: 5, bottom: 5), height: 44, width: 110, decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - HexColor.fromHex(model?.save_earn_bg1_color ?? '#FF6969'), - HexColor.fromHex(model?.save_earn_bg2_color ?? '#FF4646') - ], begin: Alignment.centerLeft, end: Alignment.centerRight), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(25), - topRight: Radius.circular(25))), + gradient: LinearGradient( + colors: [HexColor.fromHex(model?.save_earn_bg1_color ?? '#FF6969'), HexColor.fromHex(model?.save_earn_bg2_color ?? '#FF4646')], + begin: Alignment.centerLeft, + end: Alignment.centerRight), + borderRadius: BorderRadius.only(bottomRight: Radius.circular(25), topRight: Radius.circular(25))), child: Column( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ RichText( - text: TextSpan( - text: 'Â¥ ', - style: TextStyle( - fontSize: 12, - color: HexColor.fromHex( - model?.save_earn_val_color ?? 'FFFFFF')), - children: [ - TextSpan( - text: model?.self_buy_value ?? '0.0', - style: TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - color: HexColor.fromHex( - model?.save_earn_val_color ?? '#FFFFFF'), - fontFamily: 'Din', - package: 'zhiying_base_widget', - )), - ]), + text: TextSpan(text: 'Â¥ ', style: TextStyle(fontSize: 12, color: HexColor.fromHex(model?.save_earn_val_color ?? 'FFFFFF')), children: [ + TextSpan( + text: model?.self_buy_value ?? '0.0', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: HexColor.fromHex(model?.save_earn_val_color ?? '#FFFFFF'), + fontFamily: 'Din', + package: 'zhiying_base_widget', + )), + ]), ), - Text(model?.save_earn ?? '自购çœ', - style: TextStyle( - color: - HexColor.fromHex(model?.save_earn_color ?? '#FFFFFF'), - fontSize: 12)) + Text(model?.save_earn ?? '自购çœ', style: TextStyle(color: HexColor.fromHex(model?.save_earn_color ?? '#FFFFFF'), fontSize: 12)) ], ), ), @@ -376,8 +313,7 @@ class _GooddsDetailsFooterContainerState const SizedBox(height: 5), /// 图片 - Text(text, - style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 11)) + Text(text, style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 11)) ], ); } diff --git a/lib/widgets/goods_details/footer/model/goods_details_footer_model.dart b/lib/widgets/goods_details/footer/model/goods_details_footer_model.dart index 25c7224..1c7d0e2 100644 --- a/lib/widgets/goods_details/footer/model/goods_details_footer_model.dart +++ b/lib/widgets/goods_details/footer/model/goods_details_footer_model.dart @@ -25,6 +25,10 @@ class GoodsDetailsFooterModel { FavArgs favArgs; ShareUrlArgs shareUrlArgs; + String provider; + String good_id; + ConvertArgs convertArgs; + GoodsDetailsFooterModel({ this.buyUrl, this.favArgs, @@ -50,6 +54,9 @@ class GoodsDetailsFooterModel { this.share_value, this.self_buy_value, this.collected_icon, + this.good_id, + this.provider, + this.convertArgs, }); factory GoodsDetailsFooterModel.fromJson(Map json) { @@ -78,6 +85,9 @@ class GoodsDetailsFooterModel { share_value: json['share_value'], self_buy_value: json['self_buy_value'], collected_icon: json['collected_icon'], + good_id: json['good_id'], + provider: json['provider'], + convertArgs: json['convert_args'] != null ? ConvertArgs.fromJson(json['convert_args']) : null, ); } @@ -113,6 +123,12 @@ class GoodsDetailsFooterModel { data['share_url_args'] = this.shareUrlArgs.toJson(); } + data['provider'] = this.provider; + data['good_id'] = this.good_id; + if (this.convertArgs != null) { + data['convert_args'] = this.convertArgs.toJson(); + } + return data; } } @@ -218,3 +234,34 @@ class ShareUrlArgs { return data; } } + +class ConvertArgs { + String gid; + String goodUrl; + String couponUrl; + String couponPrice; + String activityUrl; + String isShare; + + ConvertArgs({this.gid, this.goodUrl, this.couponUrl, this.couponPrice, this.activityUrl, this.isShare}); + + ConvertArgs.fromJson(Map json) { + gid = json['gid']; + goodUrl = json['good_url']; + couponUrl = json['coupon_url']; + couponPrice = json['coupon_price']; + activityUrl = json['activity_url']; + isShare = json['is_share']; + } + + Map toJson() { + final Map data = new Map(); + data['gid'] = this.gid; + data['good_url'] = this.goodUrl; + data['coupon_url'] = this.couponUrl; + data['coupon_price'] = this.couponPrice; + data['activity_url'] = this.activityUrl; + data['is_share'] = this.isShare; + return data; + } +} diff --git a/lib/widgets/home/home_quick_entry/home_quick_entry.dart b/lib/widgets/home/home_quick_entry/home_quick_entry.dart index 6593405..e782b06 100644 --- a/lib/widgets/home/home_quick_entry/home_quick_entry.dart +++ b/lib/widgets/home/home_quick_entry/home_quick_entry.dart @@ -30,10 +30,10 @@ class HomeQuickEntryContianer extends StatefulWidget { } class _HomeQuickEntryContianerState extends State { - /// Icon点击事件 - void _itemIconClick(TypeNormal model){ + void _itemIconClick(TypeNormal model) { print("item type = ${model.skip_identifier}"); + // Navigator.push(context, CupertinoPageRoute(builder: (_) => CommonPage(null))); RouterUtil.route(model, model.toJson(), context); } @@ -62,7 +62,6 @@ class _HomeQuickEntryContianerState extends State { } Widget _getMainWidget(HomeQuickEntryModel model) { - // æ•°æ®æ€»æ•° int totalDataSize = model?.type_normal?.length ?? 0; @@ -97,15 +96,16 @@ class _HomeQuickEntryContianerState extends State { // 总体高度 = 行数 * (å­å…ƒç´ é«˜åº¦ + è¾¹è·é«˜åº¦) + 进度æ¡çš„高度 double totalHeight = totalRowSize * (itemHeight + barMargin) + 4; - if(!EmptyUtil.isEmpty(model?.pagination_open) && model.pagination_open == '0') { - totalHeight = totalRowSize * (itemHeight + barMargin); + if (!EmptyUtil.isEmpty(model?.pagination_open) && model.pagination_open == '0') { + totalHeight = totalRowSize * (itemHeight + barMargin); } return Container( color: Colors.white, child: Container( - margin: EdgeInsets.only(top: 15, bottom: totalPage >1 ? 15 : 0 ), - height: totalHeight, // 总体高度 + margin: EdgeInsets.only(top: 15, bottom: totalPage > 1 ? 15 : 0), + height: totalHeight, + // 总体高度 width: double.infinity, color: Colors.white, child: Swiper( @@ -122,7 +122,7 @@ class _HomeQuickEntryContianerState extends State { totalDataSize: totalDataSize, totalPage: totalPage, currentPage: index, - totalRowSize: totalRowSize, + totalRowSize: totalRowSize, columSize: columSize, model: model, itemHeight: itemHeight, @@ -136,47 +136,87 @@ class _HomeQuickEntryContianerState extends State { } /// é¡µçš„æ•°æ® - Widget _getPageWidget({double titleHeight, double iconHeight, int totalPage, int currentPage, int columSize, int totalRowSize, int totalDataSize, HomeQuickEntryModel model,double itemHeight}){ - + Widget _getPageWidget( + {double titleHeight, double iconHeight, int totalPage, int currentPage, int columSize, int totalRowSize, int totalDataSize, HomeQuickEntryModel model, double itemHeight}) { List rowList = []; - for(int i = 0 ; i < totalRowSize; i ++){ + for (int i = 0; i < totalRowSize; i++) { rowList.add(i); } - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: rowList.map((currentRow){ - return Container( - padding: EdgeInsets.only(bottom: 15), - width: double.infinity, - child: _getRowWidget(titleHeight: titleHeight, iconHeight: iconHeight, totalPage: totalPage, currentPage: currentPage, columSize: columSize, totalRowSize: totalRowSize, totalDataSize: totalDataSize, model: model, currentRow: currentRow, itemHeight: itemHeight), - ); - }).toList(), - ); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: rowList.map((currentRow) { + return Container( + padding: EdgeInsets.only(bottom: 15), + width: double.infinity, + child: _getRowWidget( + titleHeight: titleHeight, + iconHeight: iconHeight, + totalPage: totalPage, + currentPage: currentPage, + columSize: columSize, + totalRowSize: totalRowSize, + totalDataSize: totalDataSize, + model: model, + currentRow: currentRow, + itemHeight: itemHeight), + ); + }).toList(), + ); } /// è¡Œçš„æ•°æ® - Widget _getRowWidget({double titleHeight, double iconHeight, int totalPage, int currentPage, int columSize, int totalRowSize, int totalDataSize, HomeQuickEntryModel model, int currentRow, double itemHeight}) { - + Widget _getRowWidget( + {double titleHeight, + double iconHeight, + int totalPage, + int currentPage, + int columSize, + int totalRowSize, + int totalDataSize, + HomeQuickEntryModel model, + int currentRow, + double itemHeight}) { List itemList = []; - for(int i = 0; i < columSize; i++){ + for (int i = 0; i < columSize; i++) { itemList.add(i); } return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, - - children: itemList.map((currentIndex){ - return _getColumWidget(titleHeight: titleHeight, iconHeight: iconHeight, totalPage: totalPage, currentPage: currentPage, totalDataSize: totalDataSize, columSize: columSize, totalRowSize: totalRowSize, model: model, currentRow: currentRow, currentColum: currentIndex, itemHeight: itemHeight,); + children: itemList.map((currentIndex) { + return _getColumWidget( + titleHeight: titleHeight, + iconHeight: iconHeight, + totalPage: totalPage, + currentPage: currentPage, + totalDataSize: totalDataSize, + columSize: columSize, + totalRowSize: totalRowSize, + model: model, + currentRow: currentRow, + currentColum: currentIndex, + itemHeight: itemHeight, + ); }).toList(), ); } /// item çš„æ•°æ® - Widget _getColumWidget({double titleHeight, double iconHeight, int totalPage, int currentPage, int columSize, int totalRowSize, int totalDataSize, HomeQuickEntryModel model, int currentRow,int currentColum, double itemHeight}){ - + Widget _getColumWidget( + {double titleHeight, + double iconHeight, + int totalPage, + int currentPage, + int columSize, + int totalRowSize, + int totalDataSize, + HomeQuickEntryModel model, + int currentRow, + int currentColum, + double itemHeight}) { // 当å‰index = 当å‰çš„页数+1 * 当å‰çš„行数 + 当å‰çš„列数 // int currentIndex = (currentPage + 1) * currentRow + currentColum + currentRow*columSize; // int currentIndex = currentPage != 0 ? currentPage * (columSize * totalRowSize) + columSize + currentRow * columSize : @@ -187,22 +227,24 @@ class _HomeQuickEntryContianerState extends State { // print('current Index sss = $currentIndex'); - if(currentIndex >= totalDataSize){ - return Container( height: itemHeight, width: 60,); + if (currentIndex >= totalDataSize) { + return Container( + height: itemHeight, + width: 60, + ); } TypeNormal item = model?.type_normal[currentIndex]; return GestureDetector( behavior: HitTestBehavior.opaque, - onTap: ()=> _itemIconClick(item), + onTap: () => _itemIconClick(item), child: Container( height: itemHeight, width: 60, // color: Colors.red, child: Column( children: [ - /// 图标 MyNetWorkImage(item.img), @@ -211,7 +253,10 @@ class _HomeQuickEntryContianerState extends State { visible: !EmptyUtil.isEmpty(model?.title_1_open) && model.title_1_open == '1', child: Padding( padding: const EdgeInsets.only(top: 5), - child: Text(item?.title_1 ?? '', style: TextStyle( fontSize: 10, color: HexColor.fromHex(model?.title_1_text_color)),), + child: Text( + item?.title_1 ?? '', + style: TextStyle(fontSize: 10, color: HexColor.fromHex(model?.title_1_text_color)), + ), ), ), @@ -220,36 +265,39 @@ class _HomeQuickEntryContianerState extends State { visible: !EmptyUtil.isEmpty(model?.title_2_open) && model.title_2_open == '1', child: Padding( padding: const EdgeInsets.only(top: 5), - child: Text(item?.title_2 ?? '', style: TextStyle( fontSize: 10, color: HexColor.fromHex(model?.title_2_text_color)),), + child: Text( + item?.title_2 ?? '', + style: TextStyle(fontSize: 10, color: HexColor.fromHex(model?.title_2_text_color)), + ), ), ) - ], ), ), ); } - /// è¿›åº¦æ¡ - SwiperPagination _getSwiperPaginationContorl(HomeQuickEntryModel model, int pageCount){ - if(EmptyUtil.isEmpty(model?.pagination_open) || model.pagination_open == '0'){ + SwiperPagination _getSwiperPaginationContorl(HomeQuickEntryModel model, int pageCount) { + if (EmptyUtil.isEmpty(model?.pagination_open) || model.pagination_open == '0') { return null; } - if(model.pagination == 'type_point'){ + if (model.pagination == 'type_point') { // ç‚¹ç‚¹ç‚¹è¿›åº¦æ¡ return _swiperPaginationDot(model); - }else{ + } else { // è‡ªå®šä¹‰è¿›åº¦æ¡ return _swiperCustomPagination(pageCount); } - } // è¿›åº¦æ¡ åœ†å½¢ - SwiperPagination _swiperPaginationDot(HomeQuickEntryModel model){ - return SwiperPagination(margin: const EdgeInsets.only(), builder: DotSwiperPaginationBuilder( color: HexColor.fromHex(model?.pagination_unselect_color), activeColor: HexColor.fromHex(model?.pagination_select_color), size: 8, activeSize: 8)); + SwiperPagination _swiperPaginationDot(HomeQuickEntryModel model) { + return SwiperPagination( + margin: const EdgeInsets.only(), + builder: DotSwiperPaginationBuilder( + color: HexColor.fromHex(model?.pagination_unselect_color), activeColor: HexColor.fromHex(model?.pagination_select_color), size: 8, activeSize: 8)); } // è‡ªå®šä¹‰è¿›åº¦æ¡ æ¡å½¢ @@ -294,9 +342,9 @@ class _HomeQuickEntryContianerState extends State { /// 图片build 优化 /// class MyNetWorkImage extends StatelessWidget { - final String imgUrl; final double width; + const MyNetWorkImage(this.imgUrl, {this.width = 40}); @override diff --git a/lib/widgets/home/home_sreach/home_sreach_widget.dart b/lib/widgets/home/home_sreach/home_sreach_widget.dart index 0bf81a4..070ff09 100644 --- a/lib/widgets/home/home_sreach/home_sreach_widget.dart +++ b/lib/widgets/home/home_sreach/home_sreach_widget.dart @@ -71,11 +71,11 @@ class _HomeSreachContainer extends StatelessWidget { /// 打开æœç´¢é¡µ void _openSreach(BuildContext context){ - // SkipModel skipModel = SkipModel.fromJson(model); - // RouterUtil.route(skipModel, null, context); - Navigator.push(context, CupertinoPageRoute( - builder: (context) => SearchPage(null) - )); + SkipModel skipModel = SkipModel.fromJson(model); + RouterUtil.route(skipModel, skipModel.toJson(), context); + // Navigator.push(context, CupertinoPageRoute( + // builder: (context) => SearchPage(null) + // )); } @override diff --git a/lib/widgets/share/share_alert.dart b/lib/widgets/share/share_alert.dart index 67b13df..653f13c 100644 --- a/lib/widgets/share/share_alert.dart +++ b/lib/widgets/share/share_alert.dart @@ -80,7 +80,7 @@ class _ShareAlertContentState extends State<_ShareAlertContent> { Map d = modList.first; if (d != null) { String dString = d['data']; - List list = jsonDecode(dString); + List list = jsonDecode(dString)['list']; _icons = list.map((item) { return ShareIconModel.fromJson(Map.from(item)); }).toList(); diff --git a/lib/widgets/wallet_bil_detail/wallet_bil_sk.dart b/lib/widgets/wallet_bil_detail/wallet_bil_sk.dart index 4de4555..bffe15a 100644 --- a/lib/widgets/wallet_bil_detail/wallet_bil_sk.dart +++ b/lib/widgets/wallet_bil_detail/wallet_bil_sk.dart @@ -36,40 +36,27 @@ class WalletBilDetailSkeleton extends StatelessWidget { height: 16, ), Row( - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( - width: 16, - ), + Container( color: Colors.white, height: 40, width: 80, ), - SizedBox( - width: 16, - ), + Container( color: Colors.white, height: 40, width: 80, ), - SizedBox( - width: 16, - ), + Container( color: Colors.white, height: 40, width: 80, ), - SizedBox( - width: 16, - ), - Container( - color: Colors.white, - height: 40, - width: 80, - ) + ], ), Expanded(