diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json index 2ff2799..aea820a 100644 --- a/.dart_tool/package_config.json +++ b/.dart_tool/package_config.json @@ -770,7 +770,7 @@ "languageVersion": "2.1" } ], - "generated": "2020-09-19T06:19:07.938950Z", + "generated": "2020-09-21T02:27:30.928433Z", "generator": "pub", "generatorVersion": "2.7.2" } diff --git a/.idea/runConfigurations/example_lib_main_dart.xml b/.idea/runConfigurations/example_lib_main_dart.xml deleted file mode 100644 index 5fd9159..0000000 --- a/.idea/runConfigurations/example_lib_main_dart.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/saveactions_settings.xml b/.idea/saveactions_settings.xml deleted file mode 100644 index 6025467..0000000 --- a/.idea/saveactions_settings.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..13372ae Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 0000000..9d82f78 --- /dev/null +++ b/android/gradlew @@ -0,0 +1,160 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lib/pages/goods_details_page/bloc/goods_details_page_bloc.dart b/lib/pages/goods_details_page/bloc/goods_details_page_bloc.dart new file mode 100644 index 0000000..3960ec1 --- /dev/null +++ b/lib/pages/goods_details_page/bloc/goods_details_page_bloc.dart @@ -0,0 +1,39 @@ +import 'dart:async'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_repository.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; + +part 'goods_details_page_event.dart'; + +part 'goods_details_page_state.dart'; + +class GoodsDetailsPageBloc extends Bloc { + // GoodsDetailsPageBloc() : super(GoodsDetailsPageInitial()); + + @override + GoodsDetailsPageState get initialState => GoodsDetailsPageInitial(); + + GoodsDetailsPageRepository repository; + + GoodsDetailsPageBloc({@required this.repository}); + + @override + Stream mapEventToState( + GoodsDetailsPageEvent event, + ) async* { + if (event is GoodsDetailsPageInitEvent) { + yield* _mapInitEventToState(event); + } + } + + Stream _mapInitEventToState(GoodsDetailsPageInitEvent event) async* { + var result = await repository.fetchInitData(event.model); + if (!EmptyUtil.isEmpty(result)) + yield GoodsDetailsPageLoadedState(model: result); + else + yield GoodsDetailsPageErrorState(); + } +} diff --git a/lib/pages/goods_details_page/bloc/goods_details_page_event.dart b/lib/pages/goods_details_page/bloc/goods_details_page_event.dart new file mode 100644 index 0000000..4af1ac7 --- /dev/null +++ b/lib/pages/goods_details_page/bloc/goods_details_page_event.dart @@ -0,0 +1,15 @@ +part of 'goods_details_page_bloc.dart'; + +abstract class GoodsDetailsPageEvent extends Equatable { + const GoodsDetailsPageEvent(); +} + +/// 初始化 +class GoodsDetailsPageInitEvent extends GoodsDetailsPageEvent { + final Map model; + + const GoodsDetailsPageInitEvent({this.model}); + + @override + List get props => [this.model]; +} 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 new file mode 100644 index 0000000..77ed80e --- /dev/null +++ b/lib/pages/goods_details_page/bloc/goods_details_page_repository.dart @@ -0,0 +1,40 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; + +class GoodsDetailsPageRepository { + List> _pageData = []; + + /// 初始化 + Future>> fetchInitData(Map model) async { + int id = 13; + var result = await NetUtil.post('/api/v1/mod', + method: NetMethod.POST, + params: Map.from({ + 'ids': [id] + })); + try { + if(NetUtil.isSuccess(result) && !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { + return _loadData(id, result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); + } + } catch (e) { + Logger.log(e); + } + return null; + } + + /// 处理数据 + List> _loadData(int id, dynamic data) { + String key = id.toString(); + Map json = Map.from(data); + if (json.containsKey(key)) { + List list = json[key]; + _pageData = list.map((item) { + return Map.from(item); + }).toList(); + return _pageData; + } + return null; + } + + /// 获取缓存数据? + +} diff --git a/lib/pages/goods_details_page/bloc/goods_details_page_state.dart b/lib/pages/goods_details_page/bloc/goods_details_page_state.dart new file mode 100644 index 0000000..e15fc97 --- /dev/null +++ b/lib/pages/goods_details_page/bloc/goods_details_page_state.dart @@ -0,0 +1,24 @@ +part of 'goods_details_page_bloc.dart'; + +abstract class GoodsDetailsPageState extends Equatable { + const GoodsDetailsPageState(); + + @override + List get props => []; +} + +/// 初始化状态 +class GoodsDetailsPageInitial extends GoodsDetailsPageState {} + +/// 数据加载完毕状态 +class GoodsDetailsPageLoadedState extends GoodsDetailsPageState { + final List> model; + + const GoodsDetailsPageLoadedState({this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载出错 +class GoodsDetailsPageErrorState extends GoodsDetailsPageState {} diff --git a/lib/pages/goods_details_page/goods_details_page.dart b/lib/pages/goods_details_page/goods_details_page.dart new file mode 100644 index 0000000..a033f9f --- /dev/null +++ b/lib/pages/goods_details_page/goods_details_page.dart @@ -0,0 +1,193 @@ +import 'package:flutter/material.dart'; +import 'package:pull_to_refresh/pull_to_refresh.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_bloc.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/bloc/goods_details_page_repository.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/notifier/goods_details_page_notifier.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:provider/provider.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'dart:ui'; + +class GoodsDetailsPage extends StatefulWidget { + final Map data; + + GoodsDetailsPage(this.data, {Key key}) : super(key: key); + + @override + _GoodsDetailsPageState createState() => _GoodsDetailsPageState(); +} + +class _GoodsDetailsPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: HexColor.fromHex('#FFF1F1F1'), + body: MultiProvider( + providers: [ + /// 滑动通知 + ChangeNotifierProvider.value(value: GoodsDetailsPageNotifier()), + ], + child: BlocProvider( + create: (_) => GoodsDetailsPageBloc(repository: GoodsDetailsPageRepository())..add(GoodsDetailsPageInitEvent(model: widget?.data)), + child: GoodsDetailsContainer(widget?.data), + ), + ), + ); + } +} + +class GoodsDetailsContainer extends StatefulWidget { + final Map data; + + const GoodsDetailsContainer(this.data); + + @override + _GoodsDetailsContainerState createState() => _GoodsDetailsContainerState(); +} + +class _GoodsDetailsContainerState extends State { + bool _isEnded = false; + ScrollController _controller = ScrollController(); + RefreshController _refreshController = RefreshController(initialRefresh: false); + + void _onLoading() async { + // await Future.delayed(Duration(milliseconds: 1000)); + // if (mounted) setState(() {}); + // _refreshController.loadComplete(); + } + + /// 返回上一页 + void _openPop() { + print('返回上一页'); + + Navigator.maybePop(context); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + void initState() { + _controller.addListener(() { + // print('${_controller.offset} ${_controller.position.maxScrollExtent}'); + if (_controller.offset >= _controller.position.maxScrollExtent && !_isEnded) { + // 滑动到底部 + _isEnded = true; + Provider.of(context, listen: false).loadMore(); + } else if (_controller.offset < _controller.position.maxScrollExtent && _isEnded) { + _isEnded = false; + Provider.of(context, listen: false).reset(); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return MediaQuery.removePadding( + removeTop: true, + context: context, + child: Container( + width: double.infinity, + child: BlocConsumer( + listener: (BuildContext context, GoodsDetailsPageState state) { + if (state is GoodsDetailsPageErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is GoodsDetailsPageErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + print('currente state = $state'); + if (state is GoodsDetailsPageLoadedState) { + return _getMainWidget(state?.model); + } + return _getMainWidget(null); + }, + ), + ), + ); + } + + /// 主视图 + Widget _getMainWidget(List> datas) { + return Stack( + fit: StackFit.passthrough, + children: [ + /// 主体布局 + SmartRefresher( + enablePullDown: true, + enablePullUp: false, + header: WaterDropHeader(), + controller: _refreshController, + onLoading: _onLoading, + child: CustomScrollView( + controller: _controller, + slivers: _createContent(context, datas ?? []), + ), + ), + /// appBar + Align( + alignment: Alignment.topCenter, + child: _getAppBarWidget()), + /// 底部 + Align(alignment: Alignment.bottomCenter, child: GoodsDetailsFooterWidget(null)) + ], + ); + } + + List _createContent(BuildContext context, List> datas) { + List list = List(); + for (int i = 0; i < datas.length; i++) { + WidgetModel item = WidgetModel.fromJson(Map.from(datas[i])); + + print('item.modName ${item.modName}'); + list.addAll(WidgetFactory.create( + item.modName, + isSliver: true, + model: datas[i], + )); + } + if (list.length <= 0) { + list.add(SliverToBoxAdapter( + child: Container( + height: 200, + child: Center( + child: Text('暂时无数据哦~'), + ), + ), + )); + } + return list; + } + + /// appBar + Widget _getAppBarWidget() { + return Container( + width: double.infinity, + height: 40, + margin: EdgeInsets.only(top: MediaQueryData.fromWindow(window).padding.top), + child: AppBar( + backgroundColor: Colors.transparent, + elevation: 0, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios, + size: 22, + color: HexColor.fromHex('#FFFFFF'), + ), + onPressed: () => _openPop(), + ), + ), + ); + } +} diff --git a/lib/pages/goods_details_page/notifier/goods_details_page_notifier.dart b/lib/pages/goods_details_page/notifier/goods_details_page_notifier.dart new file mode 100644 index 0000000..7e041d7 --- /dev/null +++ b/lib/pages/goods_details_page/notifier/goods_details_page_notifier.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +class GoodsDetailsPageNotifier with ChangeNotifier { + bool scrollEnd = false; + + // 加载更多数据 + void loadMore() { + scrollEnd = true; + notifyListeners(); + } + + void reset() { + scrollEnd = false; + notifyListeners(); + } +} diff --git a/lib/register.dart b/lib/register.dart index 15bfe43..55a1501 100644 --- a/lib/register.dart +++ b/lib/register.dart @@ -1,3 +1,5 @@ +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; import 'package:zhiying_base_widget/pages/home_page/home_page.dart'; import 'package:zhiying_base_widget/pages/main_page/main_page.dart'; import 'package:zhiying_base_widget/pages/mine_detail_page/mine_detail_page.dart'; @@ -5,10 +7,19 @@ import 'package:zhiying_base_widget/pages/orders_page/orders_page.dart'; import 'package:zhiying_base_widget/pages/setting_page/setting_page.dart'; import 'package:zhiying_base_widget/pages/wallet_page/wallet_page.dart'; import 'package:zhiying_base_widget/widgets/home/home_auth/home_auth_creater.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/footer/goods_details_footer_widget.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/price/goods_details_price_widget.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/store_widget.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/upgrade_tip/upgrade_tip_widget.dart'; import 'package:zhiying_base_widget/widgets/home/home_banner/home_banner_creater.dart'; import 'package:zhiying_base_widget/widgets/home/home_banner/home_banner_widget.dart'; import 'package:zhiying_base_widget/widgets/home/home_goods/home_goods_creater.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/home_notice_widget.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/model/home_notice_model.dart'; import 'package:zhiying_base_widget/widgets/home/home_slide_banner/home_slide_banner_creater.dart'; +import 'package:zhiying_base_widget/widgets/home/home_sreach/home_sreach_creater.dart'; +import 'package:zhiying_base_widget/widgets/home/home_sreach/home_sreach_widget.dart'; import 'package:zhiying_base_widget/widgets/mine/mine_data/mine_data.dart'; import 'package:zhiying_base_widget/widgets/mine/mine_header/mine_header.dart'; import 'package:zhiying_base_widget/widgets/mine/mine_nav/mine_nav_bg.dart'; @@ -16,10 +27,14 @@ import 'package:zhiying_base_widget/widgets/mine/mine_nav/mine_nav_creater.dart' import 'package:zhiying_base_widget/widgets/mine/mine_quick_entry/mine_quick_entry.dart'; import 'package:zhiying_base_widget/widgets/others/normal_nav/normal_nav_creater.dart'; import 'package:zhiying_base_widget/widgets/wallet/wallet_data/wallet_data.dart'; +import 'package:zhiying_base_widget/widgets/wallet/wallet_detail/wallet_detail.dart'; import 'package:zhiying_base_widget/widgets/wallet/wallet_income/wallet_income.dart'; import 'package:zhiying_comm/util/defalut_widget_creater.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; +import 'widgets/goods_details/coupon/counpon_widget.dart'; +import 'widgets/goods_details/evaluate/goods_details_evaluate_widget.dart'; +import 'widgets/goods_details/title/goods_details_title_widget.dart'; import 'widgets/home/home_quick_entry/home_quick_entry.dart'; class BaseWidgetRegister { @@ -35,15 +50,17 @@ class BaseWidgetRegister { PageFactory.regist('index', (model) => MainPage(model)); PageFactory.regist('profile', (model) => MainPage(model)); PageFactory.regist('category', (model) => WalletPage()); + PageFactory.regist('goods_details', (model) => GoodsDetailsPage(model)); // PageFactory.regist('login', (model) => LoginPage(model)); // PageFactory.regist('login_quick', (model) => LoginQuickPage(model)); // PageFactory.regist('login_account', (model) => LoginAccountPage(model)); // PageFactory.regist('login_invite', (model) => LoginInvitePage()); - PageFactory.regist( - 'pub.flutter.profile_settings', (model) => MineDetailPage()); + PageFactory.regist('pub.flutter.profile_settings', (model) => MineDetailPage()); PageFactory.regist('pub.flutter.settings', (model) => SettingPage(model)); + PageFactory.regist('pub.flutter.my_orders', (model) => OrdersPage(model)); + } // 注册控件 @@ -53,36 +70,56 @@ class BaseWidgetRegister { WidgetFactory.regist('normal_nav', NormalNavCreater()); // ==================== 首页 -// WidgetFactory.regist('index_title', NormalNavCreater()); -// WidgetFactory.regist( -// 'index_search', DefaultWidgetCreater((model) => MineData())); - -// /// 可滚动banner + // WidgetFactory.regist('index_title', NormalNavCreater()); + /// 首页搜索栏 + // WidgetFactory.regist('index_search', HomeSreachCreater()); + // WidgetFactory.regist('index_search', DefaultWidgetCreater((model) => HomeSreachWidget(model))); + /// 可滚动banner WidgetFactory.regist('index_carousel', HomeSlideBannerCreater()); WidgetFactory.regist('index_recommend_list', GoodsListCreater()); -// /// 首页快速入口 - WidgetFactory.regist( - 'multi_nav', DefaultWidgetCreater((model) => HomeQuickEntry(model))); -// -// /// 不可以滚动banner + + /// 首页快速入口 + WidgetFactory.regist('multi_nav', DefaultWidgetCreater((model) => HomeQuickEntry(model))); + + /// 滚动公告 + WidgetFactory.regist('index_placard', DefaultWidgetCreater((model) => HomeNoticeWidget(model))); + + /// 不可以滚动banner WidgetFactory.regist('index_banner_one', HomeBannerCreater()); WidgetFactory.regist('index_banner_two', HomeBannerCreater()); WidgetFactory.regist('index_taobao_auth_tip', HomeAuthCreater()); + /// ==================== 商品详情 ==================== /// + // 商品详情轮播图 + WidgetFactory.regist('product_detail_carousel', DefaultWidgetCreater((model) => GoodsDetailsSlideBannerWidget(model))); + // 商品详情下载APP提示 + WidgetFactory.regist('product_detail_download_tips', DefaultWidgetCreater((model) => UpgradeTipWidget(model))); + // 商品详情价格显示 + WidgetFactory.regist('product_detail_price', DefaultWidgetCreater((model) => GoodsDetailsPriceWidget(model))); + // 商品详情标题 + WidgetFactory.regist('product_detail_title', DefaultWidgetCreater((model) => GoodsDetailsTitleWidget(model))); + // 商品详情优惠劵 + WidgetFactory.regist('product_detail_coupon', DefaultWidgetCreater((model) => CounponWidget(model))); + // 商品详情店铺 + WidgetFactory.regist('product_detail_shop', DefaultWidgetCreater((model) => StoreWidget(model))); + // 商品详情宝贝评价 + WidgetFactory.regist('product_detail_comment', DefaultWidgetCreater((model) => GoodsDetailsEvaluateWidget(model))); + // 商品详情图片 + // WidgetFactory.regist('product_detail_img_list', MineNavCreater()); + // 商品详情底部推荐列表 + // WidgetFactory.regist('product_detail_bottom_rec', DefaultWidgetCreater((model) => GoodsDetailsEvaluateWidget(model))); + // 商品详情底部 + WidgetFactory.regist('product_detail_bottom', DefaultWidgetCreater((model) => GoodsDetailsFooterWidget(model))); + + // ==================== 个人中心 WidgetFactory.regist('profile_appbar', MineNavCreater()); - WidgetFactory.regist('profile_background', - DefaultWidgetCreater((model) => MineNavBg(model))); - WidgetFactory.regist( - 'profile_header', DefaultWidgetCreater((model) => MineHeader(model))); - WidgetFactory.regist( - 'profile_earning', DefaultWidgetCreater((model) => MineData(model))); - WidgetFactory.regist('profile_functions', - DefaultWidgetCreater((model) => MineQuickEntry(model))); - WidgetFactory.regist('profile_my_functions', - DefaultWidgetCreater((model) => MineQuickEntry(model))); - WidgetFactory.regist('profile_carousel', - DefaultWidgetCreater((model) => HomeBannerWidget(model))); + WidgetFactory.regist('profile_background', DefaultWidgetCreater((model) => MineNavBg(model))); + WidgetFactory.regist('profile_header', DefaultWidgetCreater((model) => MineHeader(model))); + WidgetFactory.regist('profile_earning', DefaultWidgetCreater((model) => MineData(model))); + WidgetFactory.regist('profile_functions', DefaultWidgetCreater((model) => MineQuickEntry(model))); + WidgetFactory.regist('profile_my_functions', DefaultWidgetCreater((model) => MineQuickEntry(model))); + WidgetFactory.regist('profile_carousel', DefaultWidgetCreater((model) => HomeBannerWidget(model))); // ==================== 钱包 WidgetFactory.regist( @@ -90,8 +127,9 @@ class BaseWidgetRegister { // WidgetFactory.regist( // 'wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); WidgetFactory.regist('wallet_detail', HomeAuthCreater()); + WidgetFactory.regist('wallet_data', DefaultWidgetCreater((model) => WalletData())); + WidgetFactory.regist('wallet_detail', DefaultWidgetCreater((model) => WalletDetail())); - WidgetFactory.regist( - 'wallet_income', DefaultWidgetCreater((model) => WalletIncome())); + WidgetFactory.regist('wallet_income', DefaultWidgetCreater((model) => WalletIncome())); } } diff --git a/lib/widgets/goods_details/counpon_description/counpon_description_widget.dart b/lib/widgets/goods_details/counpon_description/counpon_description_widget.dart new file mode 100644 index 0000000..cbafae9 --- /dev/null +++ b/lib/widgets/goods_details/counpon_description/counpon_description_widget.dart @@ -0,0 +1,61 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 优惠券说明 +/// +class CounponDescriptionWidget extends StatelessWidget { + final Map model; + + const CounponDescriptionWidget(this.model); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 12.5), + width: double.infinity, + child:_getMainWidget(), + ); + } + + /// 主widget + Widget _getMainWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + /// 领券立减 + Padding(padding: const EdgeInsets.only(right: 5), child: _getButtom1Widget()), + + + /// 收货后返现 + Padding(padding: const EdgeInsets.only(right: 5), child: _getButtom2Widget()), + + + ], + ); + } + + /// 领券立减 + Widget _getButtom1Widget() { + return Container( + decoration: BoxDecoration( + color: HexColor.fromHex('#FFE0E0'), + borderRadius: BorderRadius.circular(50), + ), + padding: const EdgeInsets.only(left: 14, right: 14, top: 3, bottom: 3), + child: Text('领券立减100元', style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 11))); + } + + + /// 收货后返现 + Widget _getButtom2Widget() { + return Container( + decoration: BoxDecoration( + color: HexColor.fromHex('#FFEFDA'), + borderRadius: BorderRadius.circular(50), + ), + padding: const EdgeInsets.only(left: 14, right: 14, top: 3, bottom: 3), + child: Text('收货后返现5.5元', style: TextStyle(color: HexColor.fromHex('#B78107'), fontSize: 11))); + } +} diff --git a/lib/widgets/goods_details/coupon/bloc/bloc.dart b/lib/widgets/goods_details/coupon/bloc/bloc.dart new file mode 100644 index 0000000..8c610f6 --- /dev/null +++ b/lib/widgets/goods_details/coupon/bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'counpon_bloc.dart'; +export 'counpon_event.dart'; +export 'counpon_state.dart'; \ No newline at end of file diff --git a/lib/widgets/goods_details/coupon/bloc/counpon_bloc.dart b/lib/widgets/goods_details/coupon/bloc/counpon_bloc.dart new file mode 100644 index 0000000..bf024b8 --- /dev/null +++ b/lib/widgets/goods_details/coupon/bloc/counpon_bloc.dart @@ -0,0 +1,30 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/bloc/counpon_repository.dart'; +import 'bloc.dart'; + +class CounponBloc extends Bloc { + @override + CounponState get initialState => CounponInitial(); + + CounponRepository repository; + + CounponBloc({this.repository}); + + @override + Stream mapEventToState( + CounponEvent event, + ) async* { + if (event is CounponInitEvent) { + yield* _mapInitEnvetTostate(event); + } + } + + /// 获取数据 + Stream _mapInitEnvetTostate(CounponInitEvent event) async* { + var result = await repository.fetchParentData(event); + yield CounponLoadedState(model: result); + } +} diff --git a/lib/widgets/goods_details/coupon/bloc/counpon_event.dart b/lib/widgets/goods_details/coupon/bloc/counpon_event.dart new file mode 100644 index 0000000..17697b0 --- /dev/null +++ b/lib/widgets/goods_details/coupon/bloc/counpon_event.dart @@ -0,0 +1,15 @@ +import 'package:equatable/equatable.dart'; + +abstract class CounponEvent extends Equatable { + const CounponEvent(); +} + +/// 初始化请求 +class CounponInitEvent extends CounponEvent { + final Map model; + + const CounponInitEvent({this.model}); + + @override + List get props => [this.model]; +} diff --git a/lib/widgets/goods_details/coupon/bloc/counpon_repository.dart b/lib/widgets/goods_details/coupon/bloc/counpon_repository.dart new file mode 100644 index 0000000..4d5a4f3 --- /dev/null +++ b/lib/widgets/goods_details/coupon/bloc/counpon_repository.dart @@ -0,0 +1,11 @@ +import 'package:zhiying_base_widget/widgets/goods_details/coupon/bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/model/counpon_model.dart'; + +class CounponRepository { + + /// 获取父页面传进来的数据 + Future fetchParentData(CounponInitEvent event) async{ + return null; + } + +} diff --git a/lib/widgets/goods_details/coupon/bloc/counpon_state.dart b/lib/widgets/goods_details/coupon/bloc/counpon_state.dart new file mode 100644 index 0000000..8dcc9a0 --- /dev/null +++ b/lib/widgets/goods_details/coupon/bloc/counpon_state.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/model/counpon_model.dart'; + +abstract class CounponState extends Equatable { + const CounponState(); +} + +class CounponInitial extends CounponState { + @override + List get props => []; +} + +class CounponLoadedState extends CounponState { + CounponModel model; + + CounponLoadedState({this.model}); + + @override + List get props => [this.model]; +} + +class CounponErrorState extends CounponState { + @override + List get props => []; +} diff --git a/lib/widgets/goods_details/coupon/counpon_sk.dart b/lib/widgets/goods_details/coupon/counpon_sk.dart new file mode 100644 index 0000000..65e33d6 --- /dev/null +++ b/lib/widgets/goods_details/coupon/counpon_sk.dart @@ -0,0 +1,26 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +class CounponSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(left: 12.5, right: 12.5), + width: double.infinity, + height: 70, + child: _shimmerWidget(width: double.infinity, height: 65, radius: 7.5), + ); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/goods_details/coupon/counpon_widget.dart b/lib/widgets/goods_details/coupon/counpon_widget.dart new file mode 100644 index 0000000..0af9170 --- /dev/null +++ b/lib/widgets/goods_details/coupon/counpon_widget.dart @@ -0,0 +1,104 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/bloc/counpon_repository.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/counpon_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/coupon/model/counpon_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// +/// 优惠券widget +/// +class CounponWidget extends StatelessWidget { + final Map model; + + const CounponWidget(this.model); + + @override + Widget build(BuildContext context) { + return Container(); + // return BlocProvider( + // create: (_) => CounponBloc(repository: CounponRepository())..add(CounponInitEvent(model: model)), + // child: CounponContainer(), + // ); + } +} + +class CounponContainer extends StatefulWidget { + @override + _CounponContainerState createState() => _CounponContainerState(); +} + +class _CounponContainerState extends State { + /// 点击领取 + void _onJump(CounponModel model) {} + + @override + Widget build(BuildContext context) { + BlocConsumer( + listener: (context, state) {}, + buildWhen: (prev, current) { + if (current is CounponErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + if (state is CounponLoadedState) { + // return _getMainWdiget(state.model); + } + // return CounponSkeleton(); + return Container(); + }, + ); + } + + /// 主视图 + Widget _getMainWdiget(CounponModel model) { + return GestureDetector( + onTap: () => _onJump(model), + behavior: HitTestBehavior.opaque, + child: Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 12.5, right: 12.5), + padding: const EdgeInsets.only(left: 18.5), + alignment: Alignment.centerLeft, + child: Row( + children: [ + /// 价格 + _getPriceWidget(model), + const SizedBox(width: 7.5), + /// 有效期 + _getTimeWidget(model) + ], + ), + ), + ); + } + + /// 价格 + Widget _getPriceWidget(CounponModel model) { + return Row( + children: [ + /// 价格类型 + Text('¥', style: TextStyle(fontSize: 15, color: HexColor.fromHex('#FFFFFF'))), + + /// 价格 + Text('100', style: TextStyle(fontSize: 30, color: HexColor.fromHex('#FFFFFF'))), + ], + ); + } + + /// 名称与有效期 + Widget _getTimeWidget(CounponModel model) { + return Column( + children: [ + /// 标题 + Text('优惠券', style: TextStyle(fontSize: 17, color: HexColor.fromHex('#FFFFFF'))), + + /// 到期时间 + Text('有效期至2020-10-01', style: TextStyle(fontSize: 10, color: HexColor.fromHex('#FFFFFF'))) + ], + ); + } +} diff --git a/lib/widgets/goods_details/coupon/model/counpon_model.dart b/lib/widgets/goods_details/coupon/model/counpon_model.dart new file mode 100644 index 0000000..090b814 --- /dev/null +++ b/lib/widgets/goods_details/coupon/model/counpon_model.dart @@ -0,0 +1,2 @@ + +class CounponModel{} \ No newline at end of file diff --git a/lib/widgets/goods_details/evaluate/godds_details_evaluate_sk.dart b/lib/widgets/goods_details/evaluate/godds_details_evaluate_sk.dart new file mode 100644 index 0000000..bcb6668 --- /dev/null +++ b/lib/widgets/goods_details/evaluate/godds_details_evaluate_sk.dart @@ -0,0 +1,27 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +/// +/// 商品详情评论骨架屏幕 +/// +class GoodsDetailsEvaluateSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } + + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} + + diff --git a/lib/widgets/goods_details/evaluate/goods_details_evaluate_widget.dart b/lib/widgets/goods_details/evaluate/goods_details_evaluate_widget.dart new file mode 100644 index 0000000..1f404f5 --- /dev/null +++ b/lib/widgets/goods_details/evaluate/goods_details_evaluate_widget.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 商品详情评价Widget +/// +class GoodsDetailsEvaluateWidget extends StatelessWidget { + final Map model; + + const GoodsDetailsEvaluateWidget(this.model); + + /// 点击查看更多 + void _openLookMore() {} + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () => _openLookMore(), + behavior: HitTestBehavior.opaque, + child: Container( + width: double.infinity, + padding: const EdgeInsets.only(top: 15, bottom: 15, left: 12.5, right: 12.5), + child: getMainWidget(), + ), + ); + } + + /// 评价以及查看更多 + Widget getMainWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('宝贝评价', style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 12)), + Text('查看更多 >', style: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 11)), + ], + ); + } +} diff --git a/lib/widgets/goods_details/footer/goods_details_footer_sk.dart b/lib/widgets/goods_details/footer/goods_details_footer_sk.dart new file mode 100644 index 0000000..bf7e85e --- /dev/null +++ b/lib/widgets/goods_details/footer/goods_details_footer_sk.dart @@ -0,0 +1,38 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +class GoodsDetailsFooterSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + padding: const EdgeInsets.only(left: 21, right: 12.5, top: 12.5, bottom: 12.5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + _shimmerWidget(width: 30, height: 30,), + const SizedBox(width: 35), + _shimmerWidget(width: 30, height: 30,), + ], + ), + + _shimmerWidget( height: 44, width: 230, radius: 22), + ], + ), + ); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/goods_details/footer/goods_details_footer_widget.dart b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart new file mode 100644 index 0000000..1ec445c --- /dev/null +++ b/lib/widgets/goods_details/footer/goods_details_footer_widget.dart @@ -0,0 +1,169 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 商品详情底部Widget +/// +class GoodsDetailsFooterWidget extends StatelessWidget { + + final Map model; + + const GoodsDetailsFooterWidget(this.model); + + @override + Widget build(BuildContext context) { + return GooddsDetailsFooterContainer(); + } +} + +class GooddsDetailsFooterContainer extends StatefulWidget { + @override + _GooddsDetailsFooterContainerState createState() => _GooddsDetailsFooterContainerState(); +} + +class _GooddsDetailsFooterContainerState extends State { + /// 打开首页 + void _openHome() {} + + /// 收藏 + void _collectOnClick() {} + + /// 分享 + void _shareOnClick() {} + + /// 自购省 + void _savemoneyOnClick() {} + + @override + Widget build(BuildContext context) { + return Container( + height: 70, + width: double.infinity, + padding: const EdgeInsets.only(bottom: 10, top: 12.5, 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), + BoxShadow(color: Colors.grey[300], offset: Offset(0.0, 0.0)), + ], + color: Colors.white + ), + child: _getMainWidet(), + ); + } + + /// 主Widget + Widget _getMainWidet() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + /// 首页与收藏 + _getLeftWidget(), + + /// 分享赚与自购省 + _getRightWidget(), + ], + ); + } + + /// 首页 和 收藏 + Widget _getLeftWidget() { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _openHome(), + child: Padding( + padding: const EdgeInsets.only(right: 35), + child: _getCustomWidget('首页', '999999', null), + )), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _collectOnClick(), + child: Padding(padding: const EdgeInsets.only(right: 0), child: _getCustomWidget('收藏', '999999', null))) + ], + ); + } + + /// 分享赚与自购省 + Widget _getRightWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [_getFxzButton(), _getZgsButton()], + ); + } + + /// 分享赚, + Widget _getFxzButton() { + return GestureDetector( + onTap: () => _shareOnClick(), + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 30, right: 30, top: 5, bottom: 5), + decoration: BoxDecoration( + gradient: LinearGradient(colors: [HexColor.fromHex('#FFCA66'), HexColor.fromHex('#FFD961')], begin: Alignment.centerLeft, end: Alignment.centerRight), + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(25), + topLeft: Radius.circular(25) + ) + ), + child: Column( + children: [ + RichText( + text: TextSpan(text: '¥', style: TextStyle(fontSize: 12, color: HexColor.fromHex('FFFFFF')), children: [ + TextSpan(text: '3.10', style: TextStyle(fontSize: 15, color: HexColor.fromHex('#FFFFFF'))), + ]), + ), + Text('分享赚', style: TextStyle(color: HexColor.fromHex('#FFFFFF'), fontSize: 15)) + ], + ), + ), + ); + } + + /// 自购省 + Widget _getZgsButton() { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => _savemoneyOnClick(), + child: Container( + alignment: Alignment.center, + padding: const EdgeInsets.only(left: 30, right: 30, top: 5, bottom: 5), + decoration: BoxDecoration( + gradient: LinearGradient(colors: [HexColor.fromHex('#FF6969'), HexColor.fromHex('#FF4646')], begin: Alignment.centerLeft, end: Alignment.centerRight), + borderRadius: BorderRadius.only( + bottomRight: Radius.circular(25), + topRight: Radius.circular(25) + ) + ), + child: Column( + children: [ + RichText( + text: TextSpan(text: '¥', style: TextStyle(fontSize: 12, color: HexColor.fromHex('FFFFFF')), children: [ + TextSpan(text: '23.10', style: TextStyle(fontSize: 15, color: HexColor.fromHex('#FFFFFF'))), + ]), + ), + Text('自购省', style: TextStyle(color: HexColor.fromHex('#FFFFFF'), fontSize: 15)) + ], + ), + ), + ); + } + + Widget _getCustomWidget(String text, String textColor, String icon) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + /// 图标 + Container(height: 25, width: 25, color: Colors.red, margin: const EdgeInsets.only(bottom: 3)), + + /// 图片 + Text(text, style: TextStyle(color: HexColor.fromHex(textColor), fontSize: 11)) + ], + ); + } +} diff --git a/lib/widgets/goods_details/price/goods_details_price_widget.dart b/lib/widgets/goods_details/price/goods_details_price_widget.dart new file mode 100644 index 0000000..36d22ca --- /dev/null +++ b/lib/widgets/goods_details/price/goods_details_price_widget.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 商品详情价格widget +/// +class GoodsDetailsPriceWidget extends StatelessWidget { + final Map model; + + const GoodsDetailsPriceWidget(this.model); + + @override + Widget build(BuildContext context) { + return Container(margin: const EdgeInsets.symmetric(horizontal: 12.5), child: _getMainWidget()); + } + + /// 主体视图 + Widget _getMainWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _getLeftWidget(), + + /// 右边widget + _getBuyNumberWidget(), + ], + ); + } + + /// 左边的wiget 包括价格等 + Widget _getLeftWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + /// 价格 + _getPriceWidget(), + const SizedBox(width: 5), + + /// 券后 + _getQhPriceWidget(), + const SizedBox(width: 5), + + /// 积分 + _getPointsWidget() + ], + ); + } + + /// 价格 + Widget _getPriceWidget() { + return Row( + children: [ + Text('¥', style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 15)), + Text('99', style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 30)), + ], + ); + } + + /// 积分 + Widget _getPointsWidget() { + return Container( + decoration: BoxDecoration(color: HexColor.fromHex('#FFEFDA'), borderRadius: BorderRadius.circular(5)), + padding: const EdgeInsets.only(left: 4, right: 7.5, top: 4, bottom: 4), + child: Text('', style: TextStyle(color: HexColor.fromHex('#B78107'), fontSize: 9)), + ); + } + + /// 券后价格 + Widget _getQhPriceWidget() { + return Column( + children: [ + Text('券后', style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 11)), + Text('¥ 199', style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 10)), + ], + ); + } + + /// 购买人数 + Widget _getBuyNumberWidget() { + return Text('99999人已购买', style: TextStyle(color: HexColor.fromHex('#999999'), fontSize: 12.5)); + } +} diff --git a/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_bloc.dart b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_bloc.dart new file mode 100644 index 0000000..de1fd8f --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_bloc.dart @@ -0,0 +1,46 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_repository.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; + +part 'goods_details_slide_banner_event.dart'; + +part 'goods_details_slide_banner_state.dart'; + +class GoodsDetailsSlideBannerBloc extends Bloc { + GoodsDetailsSlideBannerRepository repository; + + GoodsDetailsSlideBannerBloc({@required this.repository}); + + @override + GoodsDetailsSlideBannerState get initialState => GoodsDetailsSlideBannerInitial(); + + @override + Stream mapEventToState( + GoodsDetailsSlideBannerEvent event, + ) async* { + /// 初始化 + if (event is GoodsDetailsSlideBannerInitEvent) { + yield* _mapInitEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(GoodsDetailsSlideBannerInitEvent event) async* { + var parentData = await repository.fetchParentData(event.model); + if (!EmptyUtil.isEmpty(parentData)) { + yield GoodsDetailsSlideBannerLoadedState(model: parentData); + return; + } + var netData = await repository.fetchNetData(event.model); + if (!EmptyUtil.isEmpty(netData)) + yield GoodsDetailsSlideBannerLoadedState(model: parentData); + else + yield GoodsDetailsSlideBannerErrorState(); + } +} diff --git a/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_event.dart b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_event.dart new file mode 100644 index 0000000..68ece5c --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_event.dart @@ -0,0 +1,15 @@ +part of 'goods_details_slide_banner_bloc.dart'; + +abstract class GoodsDetailsSlideBannerEvent extends Equatable { + const GoodsDetailsSlideBannerEvent(); +} + +/// 初始化事件 +class GoodsDetailsSlideBannerInitEvent extends GoodsDetailsSlideBannerEvent { + final Map model; + + const GoodsDetailsSlideBannerInitEvent({@required this.model}); + + @override + List get props => [this.model]; +} diff --git a/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_repository.dart b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_repository.dart new file mode 100644 index 0000000..c43c29a --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_repository.dart @@ -0,0 +1,21 @@ +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class GoodsDetailsSlideBannerRepository { + /// 加载父页面数据 + Future fetchParentData(final Map model) async { + if (!EmptyUtil.isEmpty(model)) { + try { + return GoodsDetailsSlideBannerModel.fromJson(model['data']); + } catch (e) { + Logger.log(e); + } + } + return null; + } + + /// 加载网络数据 + Future fetchNetData(final Map model) async { + return null; + } +} diff --git a/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_state.dart b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_state.dart new file mode 100644 index 0000000..ad4637a --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_state.dart @@ -0,0 +1,26 @@ +part of 'goods_details_slide_banner_bloc.dart'; + +abstract class GoodsDetailsSlideBannerState extends Equatable { + const GoodsDetailsSlideBannerState(); +} + +class GoodsDetailsSlideBannerInitial extends GoodsDetailsSlideBannerState { + @override + List get props => []; +} + +/// 数据加载成功 +class GoodsDetailsSlideBannerLoadedState extends GoodsDetailsSlideBannerState { + GoodsDetailsSlideBannerModel model; + + GoodsDetailsSlideBannerLoadedState({this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载失败 +class GoodsDetailsSlideBannerErrorState extends GoodsDetailsSlideBannerState { + @override + List get props => []; +} diff --git a/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_creater.dart b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_creater.dart new file mode 100644 index 0000000..aa80309 --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_creater.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 可以滚动Banner +/// +class HomeSlideBannerCreater extends WidgetCreater { + @override + List createSkeleton(Map model) { + return [GoodsDetailsSlideBannerSkeleton()]; + } + + @override + List createWidgets(Map model) { + return [GoodsDetailsSlideBannerWidget(model)]; + } +} diff --git a/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart new file mode 100644 index 0000000..fb50ce9 --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:shimmer/shimmer.dart'; + +class GoodsDetailsSlideBannerSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return _shimmerWidget(width: double.infinity, height: 375); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart new file mode 100644 index 0000000..c1abccf --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/goods_details_slide_banner_widget.dart @@ -0,0 +1,172 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_swiper/flutter_swiper.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/bloc/goods_details_slide_banner_repository.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/goods_details_slide_banner_sk.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:cached_network_image/cached_network_image.dart'; + +/// +/// 商品详情轮播图 +/// +class GoodsDetailsSlideBannerWidget extends StatelessWidget { + final Map model; + + const GoodsDetailsSlideBannerWidget(this.model, {Key key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => GoodsDetailsSlideBannerBloc(repository: GoodsDetailsSlideBannerRepository())..add(GoodsDetailsSlideBannerInitEvent(model: model)), + child: GoodsDetailsSlideBannerContainer(model), + ); + } +} + +class GoodsDetailsSlideBannerContainer extends StatefulWidget { + final Map model; + + const GoodsDetailsSlideBannerContainer(this.model, {Key key}) : super(key: key); + + @override + _GoodsDetailsSlideBannerContainerState createState() => _GoodsDetailsSlideBannerContainerState(); +} + +class _GoodsDetailsSlideBannerContainerState extends State { + /// 子元素点击事件 + void _itemOnClick(IndexCarousel model) { + print('点击了 $model'); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (BuildContext context, GoodsDetailsSlideBannerState state) { + if (state is GoodsDetailsSlideBannerErrorState) { + print('数据加载出错'); + } + }, + buildWhen: (previous, current) { + /// 数据加载出错不进行build + if (current is GoodsDetailsSlideBannerErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + print('currente state = $state'); + if (state is GoodsDetailsSlideBannerLoadedState) { + if (!EmptyUtil.isEmpty(state.model) && !EmptyUtil.isEmpty(state.model.index_carousel_list)) { + return _getMainWidget(state.model); + } + } + // 骨架屏 + return GoodsDetailsSlideBannerSkeleton(); + }, + ); + } + + /// 页面 + Widget _getMainWidget(GoodsDetailsSlideBannerModel datas) { + return Container( + width: double.infinity, + height: 375, + child: Swiper( + itemBuilder: (BuildContext context, int index) { + IndexCarousel items = datas.index_carousel_list[index]; + return Container( + width: double.infinity, + child: CachedNetworkImage(imageUrl: items?.img ?? '', fit: BoxFit.cover), + ); + }, + itemCount: datas?.index_carousel_list?.length ?? 0, + loop: true, + autoplay: true, + onTap: (index) => _itemOnClick(datas.index_carousel_list[index]), + pagination: _getSwiperStyleByType(datas, datas?.index_carousel_list?.length ?? 0), + ), + ); + } + + /// 获取进度样式 + SwiperPlugin _getSwiperStyleByType(GoodsDetailsSlideBannerModel model, int pageCount) { + if ('1' != model.pagination_open) { + return null; + } + + if ('type_number' == model.pagination) { + return _getNumswiperPlugin(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + if ('type_point' == model.pagination) { + return _swiperCustomPaginationDito(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + if ('type_bar' == model.pagination) { + return _swiperCustomPagination(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + return null; + } + + /// 数字样式 + SwiperPlugin _getNumswiperPlugin(int pageCount, String selectColor, String unselectColor) { + return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { + return Align( + alignment: Alignment(0.0, 0.9), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 18), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(13), color: HexColor.fromHex('#4D000000')), + child: RichText( + text: TextSpan(text: '${config.activeIndex + 1}', style: TextStyle(fontSize: 12, color: HexColor.fromHex(selectColor)), children: [ + TextSpan(text: '/', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), + TextSpan(text: '$pageCount', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), + ]), + )), + ); + }); + } + + /// 自定义进度条 + SwiperPlugin _swiperCustomPagination(int pageCount, String selectColor, String unselectColor) { + List list = []; + for (int i = 0; i < pageCount; i++) { + list.add(i); + } + + return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { + return Align( + alignment: Alignment(0.0, 0.9), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: list.map((index) { + var borderRadius; + if (index == 0) { + borderRadius = BorderRadius.only(topLeft: Radius.circular(2), bottomLeft: Radius.circular(2)); + } + if (index == list.length - 1) { + borderRadius = BorderRadius.only(topRight: Radius.circular(2), bottomRight: Radius.circular(2)); + } + + if (index == config.activeIndex) { + borderRadius = BorderRadius.all(Radius.circular(2)); + } + + return Container( + height: 4, + width: 25, + decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex(selectColor) : HexColor.fromHex(unselectColor)), + ); + }).toList(), + ), + ); + }); + } + + /// 圆形进度条 + SwiperPlugin _swiperCustomPaginationDito(int pageCount, String selectColor, String unselectColor) { + return SwiperPagination( + margin: const EdgeInsets.only(), + builder: DotSwiperPaginationBuilder(color: HexColor.fromHex(unselectColor), activeColor: HexColor.fromHex(selectColor), size: 8, activeSize: 8)); + } +} diff --git a/lib/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart b/lib/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart new file mode 100644 index 0000000..40f1dd9 --- /dev/null +++ b/lib/widgets/goods_details/slide_banner/model/goods_details_silde_banner_model.dart @@ -0,0 +1,61 @@ + + +class GoodsDetailsSlideBannerModel { + String pagination; + String pagination_open; + List pagination_options; + List index_carousel_list; + String pagination_select_color; + String pagination_unselect_color; + + GoodsDetailsSlideBannerModel({this.pagination, this.pagination_open, this.pagination_options, this.pagination_select_color, this.pagination_unselect_color, this.index_carousel_list}); + + factory GoodsDetailsSlideBannerModel.fromJson(Map json) { + return GoodsDetailsSlideBannerModel( + pagination: json['pagination'], + pagination_open: json['pagination_open'], + pagination_options: json['pagination_options'] != null ? new List.from(json['pagination_options']) : null, + index_carousel_list: json['index_carousel_list'] != null ? (json['index_carousel_list'] as List).map((i) => IndexCarousel.fromJson(i)).toList() : null, + pagination_select_color: json['pagination_select_color'], + pagination_unselect_color: json['pagination_unselect_color'], + + ); + } + + Map toJson() { + final Map data = new Map(); + data['pagination'] = this.pagination; + data['pagination_open'] = this.pagination_open; + data['pagination_select_color'] = this.pagination_select_color; + data['pagination_unselect_color'] = this.pagination_unselect_color; + if (this.pagination_options != null) { + data['pagination_options'] = this.pagination_options; + } + if (this.index_carousel_list != null) { + data['index_carousel_list'] = this.index_carousel_list.map((v) => v.toJson()).toList(); + } + return data; + } +} + + +class IndexCarousel { + String img; + String skip_identifier; + + IndexCarousel({this.img, this.skip_identifier}); + + factory IndexCarousel.fromJson(Map json) { + return IndexCarousel( + img: json['img'], + skip_identifier: json['skip_identifier'], + ); + } + + Map toJson() { + final Map data = new Map(); + data['img'] = this.img; + data['skip_identifier'] = this.skip_identifier; + return data; + } +} \ No newline at end of file diff --git a/lib/widgets/goods_details/store/bloc/bloc.dart b/lib/widgets/goods_details/store/bloc/bloc.dart new file mode 100644 index 0000000..38113a3 --- /dev/null +++ b/lib/widgets/goods_details/store/bloc/bloc.dart @@ -0,0 +1,4 @@ + +export 'store_bloc.dart'; +export 'store_event.dart'; +export 'store_state.dart'; \ No newline at end of file diff --git a/lib/widgets/goods_details/store/bloc/store_bloc.dart b/lib/widgets/goods_details/store/bloc/store_bloc.dart new file mode 100644 index 0000000..37a23fd --- /dev/null +++ b/lib/widgets/goods_details/store/bloc/store_bloc.dart @@ -0,0 +1,43 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/bloc/store_repository.dart'; +import 'package:zhiying_comm/util/empty_util.dart' + ''; + +import 'bloc.dart'; + +class StoreBloc extends Bloc { + StoreRepository repository; + + StoreBloc({this.repository}); + + @override + StoreState get initialState => StoreInitial(); + + @override + Stream mapEventToState( + StoreEvent event, + ) async* { + + /// 初始化 + if(event is StoreInitEvent){ + yield* _mapInitEventToState(event); + } + } + + + /// 获取数据 + Stream _mapInitEventToState(StoreInitEvent event) async*{ + var result = await repository.fetchParentData(event); + if(!EmptyUtil.isEmpty(result)){ + yield StoreLoadedState(model: result); + return; + } + var net = await repository.fetchNetData(event); + if(!EmptyUtil.isEmpty(net)) + yield StoreLoadedState(model: result); + else + yield StoreErrorState(); + } + +} diff --git a/lib/widgets/goods_details/store/bloc/store_event.dart b/lib/widgets/goods_details/store/bloc/store_event.dart new file mode 100644 index 0000000..f7c8642 --- /dev/null +++ b/lib/widgets/goods_details/store/bloc/store_event.dart @@ -0,0 +1,11 @@ +import 'package:equatable/equatable.dart'; + +abstract class StoreEvent extends Equatable { + const StoreEvent(); +} + +/// 初始化事件 +class StoreInitEvent extends StoreEvent { + @override + List get props => []; +} diff --git a/lib/widgets/goods_details/store/bloc/store_repository.dart b/lib/widgets/goods_details/store/bloc/store_repository.dart new file mode 100644 index 0000000..46d56ad --- /dev/null +++ b/lib/widgets/goods_details/store/bloc/store_repository.dart @@ -0,0 +1,10 @@ +import 'package:zhiying_base_widget/widgets/goods_details/store/bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/model/store_model.dart'; + +class StoreRepository { + /// 获取上级数据 + Future fetchParentData(StoreInitEvent event) async {} + + /// 网络数据 + Future fetchNetData(StoreInitEvent event) async {} +} diff --git a/lib/widgets/goods_details/store/bloc/store_state.dart b/lib/widgets/goods_details/store/bloc/store_state.dart new file mode 100644 index 0000000..0148013 --- /dev/null +++ b/lib/widgets/goods_details/store/bloc/store_state.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/model/store_model.dart'; + +abstract class StoreState extends Equatable { + const StoreState(); +} + +class StoreInitial extends StoreState { + @override + List get props => []; +} + +/// 数据加载完毕 +class StoreLoadedState extends StoreState { + + final StoreModel model; + const StoreLoadedState({this.model}); + + @override + List get props => []; +} + +/// 数据加载出错 +class StoreErrorState extends StoreState { + @override + List get props => []; +} diff --git a/lib/widgets/goods_details/store/model/store_model.dart b/lib/widgets/goods_details/store/model/store_model.dart new file mode 100644 index 0000000..a3200a9 --- /dev/null +++ b/lib/widgets/goods_details/store/model/store_model.dart @@ -0,0 +1,3 @@ + + +class StoreModel{} \ No newline at end of file diff --git a/lib/widgets/goods_details/store/store_sk.dart b/lib/widgets/goods_details/store/store_sk.dart new file mode 100644 index 0000000..c3017bc --- /dev/null +++ b/lib/widgets/goods_details/store/store_sk.dart @@ -0,0 +1,44 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +class StoreSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + height: 50, + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + + /// 图片 + _shimmerWidget(width: 50, height: 50), + + /// + Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + + _shimmerWidget(width: 150, height: 20), + _shimmerWidget(width: 50, height: 15) + + ], + ), + + ], + ), + ); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/goods_details/store/store_widget.dart b/lib/widgets/goods_details/store/store_widget.dart new file mode 100644 index 0000000..de3e999 --- /dev/null +++ b/lib/widgets/goods_details/store/store_widget.dart @@ -0,0 +1,147 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/bloc/store_repository.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/store/model/store_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'store_sk.dart'; + +/// +/// 商家widget +/// +class StoreWidget extends StatelessWidget { + final Map model; + + const StoreWidget(this.model); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => StoreBloc(repository: StoreRepository())..add(StoreInitEvent()), + child: StoreContainer(), + ); + } +} + +class StoreContainer extends StatefulWidget { + @override + _StoreContainerState createState() => _StoreContainerState(); +} + +class _StoreContainerState extends State { + + /// 点击更多 + void _onMoreClick(){ + + } + + /// 点击商家 + void _onStoreClick(){ + + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) {}, + buildWhen: (prev, current) { + if (current is StoreErrorState) { + return false; + } + return true; + }, + builder: (context, state) { + if (state is StoreLoadedState) { + return _getMianWidget(state.model); + } + return StoreSkeleton(); + }, + ); + } + + /// 主视图 + Widget _getMianWidget(StoreModel model) { + return Container( + margin: const EdgeInsets.only(left: 12.5, right: 12.5), + child: Row( + children: [ + /// 商家图片 + _getStoreImgWidget(model), + + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + /// 商店名称与更多 + _getStoreNameWidget(model), + + /// 评分描述 + _getEvaluateWidget(model), + ], + ), + ), + ], + ), + ); + } + + /// 商家图片 + Widget _getStoreImgWidget(StoreModel model) { + return Container( + width: 50, + height: 50, + color: Colors.red, + ); + } + + /// 商店名称 + Widget _getStoreNameWidget(StoreModel model) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + /// 商店名称 + Text( + '品胜京东自营旗舰店', + style: TextStyle(color: HexColor.fromHex('#333333'), fontSize: 13), + ), + + /// 更多 + Text( + '更多店铺优惠 >', + style: TextStyle(color: HexColor.fromHex('#FF4242'), fontSize: 11), + ), + ], + ); + } + + /// 评分描述 + Widget _getEvaluateWidget(StoreModel model) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + /// 宝贝描述 5.0 + _getCoustomWidet('宝贝描述 5.0', '#999999', ''), + + /// 物流服务 5.0 + _getCoustomWidet('宝贝描述 5.0', '#999999', ''), + + /// 服务态度 1.0 + _getCoustomWidet('宝贝描述 5.0', '#999999', ''), + ], + ); + } + + Widget _getCoustomWidet(String text, String textColor, String icon) { + return Row( + children: [ + Text(text, style: TextStyle(fontSize: 11, color: HexColor.fromHex(textColor))), + Container( + width: 12, + height: 12, + color: Colors.red, + ), + ], + ); + } +} diff --git a/lib/widgets/goods_details/title/goods_details_title_widget.dart b/lib/widgets/goods_details/title/goods_details_title_widget.dart new file mode 100644 index 0000000..17a9a32 --- /dev/null +++ b/lib/widgets/goods_details/title/goods_details_title_widget.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 商品详情标题 +/// +class GoodsDetailsTitleWidget extends StatelessWidget { + final Map model; + + const GoodsDetailsTitleWidget(this.model); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + margin: const EdgeInsets.only(left: 12.5, right: 12.5), + child: _getMaiWidget(), + ); + } + + /// 主widget + Widget _getMaiWidget() { + return RichText( + maxLines: 2, + overflow: TextOverflow.ellipsis, + text: TextSpan(children: [ + WidgetSpan( + child: _getGoodsTypeIcon(), + ), + _getGoodsTitle(), + ]), + ); + } + + /// 商品类型图标 + Widget _getGoodsTypeIcon() { + return Container( + height: 15, + width: 32, + color: Colors.red, + ); + } + + /// 商品标题 + InlineSpan _getGoodsTitle() { + return TextSpan(text: '品胜(PISEN)苹果数据线1.5米 适用于苹果手机所有机型 MFI认证安全稳定一年换新1.5米2米', style: TextStyle(fontSize: 14, color: HexColor.fromHex('#FF333333'))); + } +} diff --git a/lib/widgets/goods_details/upgrade_tip/model/upgrade_tip_model.dart b/lib/widgets/goods_details/upgrade_tip/model/upgrade_tip_model.dart new file mode 100644 index 0000000..0d86b2f --- /dev/null +++ b/lib/widgets/goods_details/upgrade_tip/model/upgrade_tip_model.dart @@ -0,0 +1,3 @@ + +class UpgradeTipModel{} + diff --git a/lib/widgets/goods_details/upgrade_tip/upgrade_tip_sk.dart b/lib/widgets/goods_details/upgrade_tip/upgrade_tip_sk.dart new file mode 100644 index 0000000..b576576 --- /dev/null +++ b/lib/widgets/goods_details/upgrade_tip/upgrade_tip_sk.dart @@ -0,0 +1,27 @@ + +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +class UpgradeTipSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 12.5), + height: 35, + child: _shimmerWidget(width: double.infinity, height: double.infinity, radius: 20), + ); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} + diff --git a/lib/widgets/goods_details/upgrade_tip/upgrade_tip_widget.dart b/lib/widgets/goods_details/upgrade_tip/upgrade_tip_widget.dart new file mode 100644 index 0000000..9f9e69e --- /dev/null +++ b/lib/widgets/goods_details/upgrade_tip/upgrade_tip_widget.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/goods_details/upgrade_tip/model/upgrade_tip_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +/// +/// 更新提示widget +/// +class UpgradeTipWidget extends StatelessWidget { + final Map model; + + const UpgradeTipWidget(this.model); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: HexColor.fromHex('#FFEFDA'), + borderRadius: BorderRadius.circular(30), + ), + padding: const EdgeInsets.only(left: 10, right: 13, top: 10, bottom: 10), + margin: const EdgeInsets.symmetric(horizontal: 12.5), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _geLeftWidget(null), + _getRightWidget(null), + ], + ), + ); + } + + /// 左边的视图 + Widget _geLeftWidget(UpgradeTipModel model) { + return Row( + children: [ + /// 图标 + Container(width: 15, height: 15, color: Colors.red), + + const SizedBox(width: 7.5), + + /// 文字 + Text('下载APP升级运营商,享受更多收益', style: TextStyle(color: HexColor.fromHex('#C09023'), fontSize: 11)) + ], + ); + } + + /// 右边的视图 + Widget _getRightWidget(UpgradeTipModel model) { + return Row( + children: [ + Text('前往下载', style: TextStyle(color: HexColor.fromHex('#C09023'), fontSize: 11)), + const SizedBox(width: 4), + Text('》', style: TextStyle(color: HexColor.fromHex('#C09023'), fontSize: 11)) + ], + ); + } +} diff --git a/lib/widgets/home/home_ai_dialog/home_ai_dialog.dart b/lib/widgets/home/home_ai_dialog/home_ai_dialog.dart new file mode 100644 index 0000000..f84b59a --- /dev/null +++ b/lib/widgets/home/home_ai_dialog/home_ai_dialog.dart @@ -0,0 +1,9 @@ +import 'package:flutter/material.dart'; + + +class HomeAiDialog extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } +} diff --git a/lib/widgets/home/home_banner/home_banner_widget.dart b/lib/widgets/home/home_banner/home_banner_widget.dart index bda962a..34ca54b 100644 --- a/lib/widgets/home/home_banner/home_banner_widget.dart +++ b/lib/widgets/home/home_banner/home_banner_widget.dart @@ -45,6 +45,7 @@ class _HomeBannerContainerState extends State { /// 点击事件 void _itemOnClick(HomeBannerListItemModel model){ print('${model?.skip_identifier}'); + RouterUtil.route(model.toJson(), context); } @override diff --git a/lib/widgets/home/home_notice/bloc/bloc.dart b/lib/widgets/home/home_notice/bloc/bloc.dart new file mode 100644 index 0000000..d68273b --- /dev/null +++ b/lib/widgets/home/home_notice/bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'home_notice_bloc.dart'; +export 'home_notice_event.dart'; +export 'home_notice_state.dart'; \ No newline at end of file diff --git a/lib/widgets/home/home_notice/bloc/home_notice_bloc.dart b/lib/widgets/home/home_notice/bloc/home_notice_bloc.dart new file mode 100644 index 0000000..56a2cab --- /dev/null +++ b/lib/widgets/home/home_notice/bloc/home_notice_bloc.dart @@ -0,0 +1,32 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/bloc/home_notice_repository.dart'; +import 'package:zhiying_comm/util/empty_util.dart'; +import 'bloc.dart'; + +class HomeNoticeBloc extends Bloc { + HomeNoticeRepository repository; + + HomeNoticeBloc({this.repository}); + + @override + HomeNoticeState get initialState => HomeNoticeInitial(); + + @override + Stream mapEventToState( + HomeNoticeEvent event, + ) async* { + if(event is HomeNoticeInitEvent){ + yield* _mapInitEventToState(event); + } + } + + /// 初始化 + Stream _mapInitEventToState(HomeNoticeInitEvent event) async* { + var parentData = await repository.fetchParentData(event); + if(!EmptyUtil.isEmpty(parentData)) + yield HomeNoticeLoadedState(model: parentData); + else + yield HomeNoticeErrorState(); + } +} diff --git a/lib/widgets/home/home_notice/bloc/home_notice_event.dart b/lib/widgets/home/home_notice/bloc/home_notice_event.dart new file mode 100644 index 0000000..7b5c1e0 --- /dev/null +++ b/lib/widgets/home/home_notice/bloc/home_notice_event.dart @@ -0,0 +1,16 @@ +import 'package:equatable/equatable.dart'; +import 'bloc.dart'; + +abstract class HomeNoticeEvent extends Equatable { + const HomeNoticeEvent(); + @override + List get props => []; +} + +/// 初始化事件 +class HomeNoticeInitEvent extends HomeNoticeEvent{ + final Map model; + const HomeNoticeInitEvent({this.model}); + @override + List get props => [this.model]; +} \ No newline at end of file diff --git a/lib/widgets/home/home_notice/bloc/home_notice_repository.dart b/lib/widgets/home/home_notice/bloc/home_notice_repository.dart new file mode 100644 index 0000000..c17af8c --- /dev/null +++ b/lib/widgets/home/home_notice/bloc/home_notice_repository.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; + +import 'package:zhiying_base_widget/widgets/home/home_notice/bloc/bloc.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/model/home_notice_model.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class HomeNoticeRepository { + /// 获取父页面的数据 + Future fetchParentData(HomeNoticeInitEvent event) async { + try { + String jsonStr = event.model['data']; + return HomeNoticeModel.fromJson(jsonDecode(jsonStr)); + } catch (e) { + Logger.log(e); + } + return null; + } +} diff --git a/lib/widgets/home/home_notice/bloc/home_notice_state.dart b/lib/widgets/home/home_notice/bloc/home_notice_state.dart new file mode 100644 index 0000000..e6e4dd3 --- /dev/null +++ b/lib/widgets/home/home_notice/bloc/home_notice_state.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/model/home_notice_model.dart'; +import 'bloc.dart'; + +abstract class HomeNoticeState extends Equatable { + const HomeNoticeState(); + @override + List get props => []; +} + +/// 初始化事件 +class HomeNoticeInitial extends HomeNoticeState { + @override + List get props => []; +} + +/// 数据加载完毕 +class HomeNoticeLoadedState extends HomeNoticeState { + final HomeNoticeModel model; + + const HomeNoticeLoadedState({this.model}); + + @override + List get props => [this.model]; +} + +/// 数据加载出错 +class HomeNoticeErrorState extends HomeNoticeState{ + +} diff --git a/lib/widgets/home/home_notice/home_notice_sk.dart b/lib/widgets/home/home_notice/home_notice_sk.dart new file mode 100644 index 0000000..32e21f8 --- /dev/null +++ b/lib/widgets/home/home_notice/home_notice_sk.dart @@ -0,0 +1,28 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +/// +/// 公告的骨架屏 +/// +class HomeNoticeSkeleton extends StatelessWidget { + final Map map; + + const HomeNoticeSkeleton({this.map}); + + @override + Widget build(BuildContext context) { + return Container(padding: const EdgeInsets.symmetric(horizontal: 12.5), child: _shimmerWidget(width: double.infinity, height: 30, radius: 8)); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/home/home_notice/home_notice_widget.dart b/lib/widgets/home/home_notice/home_notice_widget.dart new file mode 100644 index 0000000..7a93f70 --- /dev/null +++ b/lib/widgets/home/home_notice/home_notice_widget.dart @@ -0,0 +1,203 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/bloc/home_notice_repository.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/home_notice_sk.dart'; +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:zhiying_base_widget/widgets/home/home_notice/model/home_notice_model.dart'; +import 'bloc/bloc.dart'; + +/// +/// 公告滚动widget +/// +class HomeNoticeWidget extends StatelessWidget { + final Map model; + + const HomeNoticeWidget(this.model); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) => HomeNoticeBloc(repository: HomeNoticeRepository())..add(HomeNoticeInitEvent(model: model)), + child: HomeNoticeWidgetContianer(), + ); + } +} + +class HomeNoticeWidgetContianer extends StatefulWidget { + @override + _HomeNoticeWidgetContianerState createState() => _HomeNoticeWidgetContianerState(); +} + +class _HomeNoticeWidgetContianerState extends State { + + /// 子item点击事件 + void _itemOnClick(HomeNoticeModel model) { + if(pageIndex == model.notices.length){ + pageIndex = 0; + } + print('===== $pageIndex'); + HomeNoticeNoticesModel item = model.notices[pageIndex]; + _itemJump(item); + } + + /// 子item跳转 + void _itemJump(HomeNoticeNoticesModel model){ + print('${model?.skip_identifier}'); + RouterUtil.route(model.toJson(), context); + } + + @override + Widget build(BuildContext context) { + return BlocConsumer( + listener: (context, state) {}, + buildWhen: (prev, current) { + return true; + }, + builder: (context, state) { + if (state is HomeNoticeLoadedState) { + return _getMainWidget(state?.model); + } + return HomeNoticeSkeleton(); + }, + ); + } + + /// 主体页面 + Widget _getMainWidget(HomeNoticeModel model) { + return Container( + width: double.infinity, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.only(bottomLeft: Radius.circular(7.5), bottomRight: Radius.circular(7.5))), + padding: const EdgeInsets.only(bottom: 7.5), + child: Container( + decoration: BoxDecoration(borderRadius: BorderRadius.circular(7.5), color: HexColor.fromHex(model?.bg_color)), + // color: Colors.cyan), + margin: const EdgeInsets.only(left: 12.5, right: 12.5), + padding: const EdgeInsets.only(top: 8, bottom: 8, left: 12, right: 8), + // height: 30, + width: double.infinity, + child: _getChildWidget(model), + ), + ); + } + + var pageIndex = 0; + + Widget _getChildWidget(HomeNoticeModel model) { + return GestureDetector( + onTap: () => _itemOnClick(model), + behavior: HitTestBehavior.opaque, + child: Row( + children: [ + /// 图片 + // Container(width: 52, height: 13, color: Colors.red), + CachedNetworkImage( + imageUrl: model?.logo_img ?? '', + width: 52, + ), + const SizedBox(width: 14), + + /// 文字 + Expanded( + child: Container( + width: double.infinity, + height: 15, + alignment: Alignment.centerLeft, + // color: Colors.yellowAccent, + child: MarqueeWidget( + model?.notices?.length ?? 0, + (BuildContext context, int index) { + HomeNoticeNoticesModel item = model.notices[index]; + return Align(alignment: Alignment.centerLeft, child: Text('${item?.notice_text}' , style: TextStyle(color: HexColor.fromHex(model?.text_color), fontSize: 12))); + }, + onPageChanged: (index) => pageIndex = index, + ), + ), + ), + const SizedBox(width: 14), + + /// 图片 + CachedNetworkImage(imageUrl: model?.jump_img ?? '', height: 18), + // Container( + // width: 18, + // height: 18, + // color: Colors.red, + // ), + ], + ), + ); + } +} + +// 上下滚动的消息轮播 +class MarqueeWidget extends StatefulWidget { + int count; // 子视图数量 + IndexedWidgetBuilder itemBuilder; // 子视图构造器 + final ValueChanged onPageChanged; + + MarqueeWidget(this.count, this.itemBuilder, {this.onPageChanged}); + + @override + _MarqueeWidgetState createState() => _MarqueeWidgetState(); +} + +class _MarqueeWidgetState extends State { + PageController _controller; + Timer _timer; + + @override + void initState() { + super.initState(); + + if (widget.count > 1) { + _controller = PageController(); + _timer = Timer.periodic(Duration(seconds: 5), (timer) { + // 如果当前位于最后一页,则直接跳转到第一页,两者内容相同,跳转时视觉上无感知 + if (_controller.page.round() >= widget.count) { + _controller.jumpToPage(0); + } + _controller.nextPage(duration: Duration(seconds: 1), curve: Curves.linear); + }); + } + } + + @override + Widget build(BuildContext context) { + // return PageView.builder( + // scrollDirection: Axis.vertical, + // controller: _controller, + // itemBuilder: (buildContext, index) { + // if (index < widget.count) { + // return widget.itemBuilder(buildContext, index); + // } else { + // return widget.itemBuilder(buildContext, 0); + // } + // }, + // itemCount: widget.count + 1, // 在原数据末尾添加一笔数据(即第一笔数据),用于实现无限循环滚动效果 + // ); + // } + + return PageView.custom( + physics: NeverScrollableScrollPhysics(), + childrenDelegate: SliverChildBuilderDelegate((context, index) { + if (index < widget.count) { + return widget.itemBuilder(context, index); + } else { + return widget.itemBuilder(context, 0); + } + }, childCount: widget.count + 1), + scrollDirection: Axis.vertical, + controller: _controller, + onPageChanged: widget?.onPageChanged, + ); + } + + @override + void dispose() { + super.dispose(); + _controller?.dispose(); + _timer?.cancel(); + } +} diff --git a/lib/widgets/home/home_notice/model/home_notice_model.dart b/lib/widgets/home/home_notice/model/home_notice_model.dart new file mode 100644 index 0000000..1ce3866 --- /dev/null +++ b/lib/widgets/home/home_notice/model/home_notice_model.dart @@ -0,0 +1,58 @@ +class HomeNoticeModel { + String logo_img; + String jump_img; + String bg_color; + String text_color; + String duration_time; + + List notices; + + HomeNoticeModel({this.notices, this.logo_img, this.jump_img, this.bg_color, this.text_color, this.duration_time}); + + factory HomeNoticeModel.fromJson(Map json) { + return HomeNoticeModel( + logo_img: json['logo_img']?.toString(), + jump_img: json['jump_img']?.toString(), + bg_color: json['bg_color']?.toString(), + text_color: json['text_color']?.toString(), + duration_time: json['duration_time']?.toString(), + notices: json['notices'] != null ? (json['notices'] as List).map((i) => HomeNoticeNoticesModel.fromJson(i)).toList() : null, + ); + } + + Map toJson() { + final Map data = new Map(); + + data['duration_time'] = this.duration_time; + data['text_color'] = this.text_color; + data['bg_color'] = this.bg_color; + data['jump_img'] = this.jump_img; + data['logo_img'] = this.logo_img; + + if (this.notices != null) { + data['notices'] = this.notices.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class HomeNoticeNoticesModel { + String notice_text; + String skip_identifier; + + HomeNoticeNoticesModel({this.notice_text, this.skip_identifier}); + + factory HomeNoticeNoticesModel.fromJson(Map json) { + return HomeNoticeNoticesModel( + notice_text: json['notice_text']?.toString(), + skip_identifier: json['skip_identifier']?.toString(), + ); + } + + Map toJson() { + final Map data = new Map(); + data['img'] = this.notice_text; + data['skip_identifier'] = this.skip_identifier; + 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 6d9723a..8689a25 100644 --- a/lib/widgets/home/home_quick_entry/home_quick_entry.dart +++ b/lib/widgets/home/home_quick_entry/home_quick_entry.dart @@ -32,8 +32,9 @@ class HomeQuickEntryContianer extends StatefulWidget { class _HomeQuickEntryContianerState extends State { /// Icon点击事件 - void _itemIconClick(TypeNormal item){ - print("item type = ${item.skip_identifier}"); + void _itemIconClick(TypeNormal model){ + print("item type = ${model.skip_identifier}"); + RouterUtil.route(model.toJson(), context); } @override @@ -250,6 +251,7 @@ class _HomeQuickEntryContianerState extends State { 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)); } + // 自定义进度条 条形 SwiperPlugin _swiperCustomPagination(int pageCount) { List list = []; diff --git a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_bloc.dart b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_bloc.dart index 5b6b786..12d2c6d 100644 --- a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_bloc.dart +++ b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_bloc.dart @@ -9,8 +9,7 @@ import 'package:zhiying_comm/util/empty_util.dart'; import './bloc.dart'; -class HomeSlideBannerBloc - extends Bloc { +class HomeSlideBannerBloc extends Bloc { HomeSlideBannerRepository repository; HomeSlideBannerBloc({@required this.repository}); @@ -19,8 +18,7 @@ class HomeSlideBannerBloc HomeSlideBannerState get initialState => InitialHomeSlideBannerState(); @override - Stream mapEventToState( - HomeSlideBannerEvent event) async* { + Stream mapEventToState(HomeSlideBannerEvent event) async* { final currentState = state; /// 初始化 @@ -31,11 +29,14 @@ class HomeSlideBannerBloc } /// 初始化 - Stream _mapInitEventToState( - HomeBannerInitEvent event) async* { + Stream _mapInitEventToState(HomeBannerInitEvent event) async* { + var parent = await repository.fetchPreantData(event.model); + if(!EmptyUtil.isEmpty(parent)){ + yield HomeSlideBannerLoadedState(datas: parent); + return; + } var cached = await repository.fetchCachedDate(id: event.model['mod_id']); - if (!EmptyUtil.isEmpty(cached)) - yield HomeSlideBannerCachedState(datas: cached); + if (!EmptyUtil.isEmpty(cached)) yield HomeSlideBannerCachedState(datas: cached); var param = await repository.fetchData(id: event.model['mod_id']); if (!EmptyUtil.isEmpty(param)) yield HomeSlideBannerLoadedState(datas: param); diff --git a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_repository.dart b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_repository.dart index a396ff9..37a978a 100644 --- a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_repository.dart +++ b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_repository.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import 'package:flutter/cupertino.dart'; import 'package:zhiying_base_widget/widgets/home/home_slide_banner/model/home_slide_banner_model.dart'; import 'package:zhiying_comm/util/empty_util.dart'; @@ -6,33 +8,42 @@ import 'package:zhiying_comm/util/net_util.dart'; class HomeSlideBannerRepository { /// 获取缓存数据 - Future> fetchCachedDate({@required int id}) async { + Future fetchCachedDate({@required int id}) async { var cached = await NetUtil.getRequestCachedData('/api/v1/mod', params: { 'ids': [id] }); if (!EmptyUtil.isEmpty(cached)) { - HomeSlideBannerModel model = HomeSlideBannerModel.fromJson(cached); - if (null != model && !EmptyUtil.isEmpty(model.items)) { - return model.items; + try { + return HomeSlideBannerModel.fromJson(cached); + }catch(e){ } + } return null; } /// 获取父页面传进来的数据 - + Future fetchPreantData(@required Map model) async{ + try{ + if(!EmptyUtil.isEmpty(model)){ + return HomeSlideBannerModel.fromJson(jsonDecode(model['data'])); + } + }catch(e){ + } + return null; + } /// 获取数据 - Future> fetchData({@required int id}) async { + Future fetchData({@required int id}) async { var params = await NetUtil.post('/api/v1/mod', params: { 'ids': [id] }, cache: true); if (NetUtil.isSuccess(params) && !EmptyUtil.isEmpty(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) { - HomeSlideBannerModel model = HomeSlideBannerModel.fromJson(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); - if (null != model && !EmptyUtil.isEmpty(model.items)) { - return model.items; + try{ + return HomeSlideBannerModel.fromJson(params[GlobalConfig.HTTP_RESPONSE_KEY_DATA]); + }catch(e){ } } return null; diff --git a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_state.dart b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_state.dart index 5765ab1..e53e534 100644 --- a/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_state.dart +++ b/lib/widgets/home/home_slide_banner/bloc/home_slide_banner_state.dart @@ -16,7 +16,7 @@ class InitialHomeSlideBannerState extends HomeSlideBannerState { /// 缓存数据 class HomeSlideBannerCachedState extends HomeSlideBannerState { - List datas; + HomeSlideBannerModel datas; HomeSlideBannerCachedState({this.datas}); @@ -26,12 +26,11 @@ class HomeSlideBannerCachedState extends HomeSlideBannerState { /// 数据加载完毕状态 class HomeSlideBannerLoadedState extends HomeSlideBannerState { - List datas; + HomeSlideBannerModel datas; HomeSlideBannerLoadedState({this.datas}); - HomeSlideBannerLoadedState copyWith( - {List newData}) { + HomeSlideBannerLoadedState copyWith({HomeSlideBannerModel newData}) { return HomeSlideBannerLoadedState( datas: newData ?? datas, ); diff --git a/lib/widgets/home/home_slide_banner/home_slide_banner.dart b/lib/widgets/home/home_slide_banner/home_slide_banner.dart index 5bdbc73..9c1fd78 100644 --- a/lib/widgets/home/home_slide_banner/home_slide_banner.dart +++ b/lib/widgets/home/home_slide_banner/home_slide_banner.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_swiper/flutter_swiper.dart'; import 'package:provider/provider.dart'; +import 'package:zhiying_base_widget/pages/goods_details_page/goods_details_page.dart'; import 'package:zhiying_base_widget/pages/main_page/notifier/main_page_bg_notifier.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:zhiying_comm/zhiying_comm.dart'; @@ -39,9 +40,14 @@ class HomeSlideBannerContainer extends StatefulWidget { } class _HomeSlideBannerContainerState extends State { + /// 子元素点击事件 - void _itemOnClick(HomeSlideBannerModelItems items) { - print('点击了 $items'); + void _itemOnClick(IndexCarousel model) { + print('点击了 $model'); + // RouterUtil.route(model.toJson(), context); + Navigator.push(context, MaterialPageRoute( + builder: (_) => PageFactory.create('goods_details', null) + )); } @override @@ -73,26 +79,26 @@ class _HomeSlideBannerContainerState extends State { ); } - Widget _getMainWidget(List datas) { + Widget _getMainWidget(HomeSlideBannerModel datas) { return Container( width: double.infinity, height: 140, child: Swiper( itemBuilder: (BuildContext context, int index) { - HomeSlideBannerModelItems items = datas[index]; + IndexCarousel items = datas.index_carousel_list[index]; return Container( - // color: Colors.primaries[index % Colors.primaries.length], width: double.infinity, child: CachedNetworkImage( - imageUrl: items?.img?? '', + imageUrl: items?.img ?? '', fit: BoxFit.cover, ), ); }, - itemCount: datas?.length ?? 0, + itemCount: datas?.index_carousel_list?.length ?? 0, loop: true, - onTap: (index) => _itemOnClick(datas[index]), - pagination: _SwiperCustomPagination(datas?.length ?? 0), + autoplay: true, + onTap: (index) => _itemOnClick(datas.index_carousel_list[index]), + pagination: _getSwiperStyleByType(datas, datas?.index_carousel_list?.length ?? 0), onIndexChanged: (index) { print('切换下一页'); Provider.of(context, listen: false).switchBg(Container( @@ -105,8 +111,45 @@ class _HomeSlideBannerContainerState extends State { ); } + /// 获取进度样式 + SwiperPlugin _getSwiperStyleByType(HomeSlideBannerModel model, int pageCount) { + + if('1' != model.pagination_open){ + return null; + } + + if ('type_number' == model.pagination) { + return _getNumswiperPlugin(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + if ('type_point' == model.pagination) { + return _swiperCustomPaginationDito(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + if ('type_bar' == model.pagination) { + return _swiperCustomPagination(pageCount, model.pagination_select_color, model.pagination_unselect_color); + } + return null; + } + + /// 数字样式 + SwiperPlugin _getNumswiperPlugin(int pageCount, String selectColor, String unselectColor) { + return SwiperCustomPagination(builder: (BuildContext context, SwiperPluginConfig config) { + return Align( + alignment: Alignment(0.0, 0.9), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 18), + decoration: BoxDecoration(borderRadius: BorderRadius.circular(13), color: HexColor.fromHex('#4D000000')), + child: RichText( + text: TextSpan(text: '${config.activeIndex + 1}', style: TextStyle(fontSize: 12, color: HexColor.fromHex(selectColor)), children: [ + TextSpan(text: '/', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), + TextSpan(text: '$pageCount', style: TextStyle(fontSize: 12, color: HexColor.fromHex(unselectColor))), + ]), + )), + ); + }); + } + /// 自定义进度条 - SwiperPlugin _SwiperCustomPagination(int pageCount) { + SwiperPlugin _swiperCustomPagination(int pageCount, String selectColor, String unselectColor) { List list = []; for (int i = 0; i < pageCount; i++) { list.add(i); @@ -134,11 +177,16 @@ class _HomeSlideBannerContainerState extends State { return Container( height: 4, width: 25, - decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex('#FF4242') : HexColor.fromHex('#FFFFFF')), + decoration: BoxDecoration(borderRadius: borderRadius, color: index == config.activeIndex ? HexColor.fromHex(selectColor) : HexColor.fromHex(unselectColor)), ); }).toList(), ), ); }); } + + /// 圆形进度条 + SwiperPlugin _swiperCustomPaginationDito(int pageCount, String selectColor, String unselectColor) { + return SwiperPagination(margin: const EdgeInsets.only(), builder: DotSwiperPaginationBuilder( color: HexColor.fromHex(unselectColor), activeColor: HexColor.fromHex(selectColor), size: 8, activeSize: 8)); + } } diff --git a/lib/widgets/home/home_slide_banner/model/home_slide_banner_model.dart b/lib/widgets/home/home_slide_banner/model/home_slide_banner_model.dart index cdcda54..9165299 100644 --- a/lib/widgets/home/home_slide_banner/model/home_slide_banner_model.dart +++ b/lib/widgets/home/home_slide_banner/model/home_slide_banner_model.dart @@ -1,115 +1,58 @@ + class HomeSlideBannerModel { - List items; + List index_carousel_list; + String pagination; + String pagination_open; + List pagination_options; + String pagination_select_color; + String pagination_unselect_color; - HomeSlideBannerModel({this.items}); + HomeSlideBannerModel({this.index_carousel_list, this.pagination, this.pagination_open, this.pagination_options, this.pagination_select_color, this.pagination_unselect_color}); - HomeSlideBannerModel.fromJson(Map json) { - if (json['6'] != null) { - items = new List(); - json['6'].forEach((v) { - items.add(new HomeSlideBannerModelItems.fromJson(v)); - }); + factory HomeSlideBannerModel.fromJson(Map json) { + return HomeSlideBannerModel( + index_carousel_list: json['index_carousel_list'] != null ? (json['index_carousel_list'] as List).map((i) => IndexCarousel.fromJson(i)).toList() : null, + pagination: json['pagination'], + pagination_open: json['pagination_open'], + pagination_options: json['pagination_options'] != null ? new List.from(json['pagination_options']) : null, + pagination_select_color: json['pagination_select_color'], + pagination_unselect_color: json['pagination_unselect_color'], + ); } - } - Map toJson() { - final Map data = new Map(); - if (this.items != null) { - data['6'] = this.items.map((v) => v.toJson()).toList(); + Map toJson() { + final Map data = new Map(); + data['pagination'] = this.pagination; + data['pagination_open'] = this.pagination_open; + data['pagination_select_color'] = this.pagination_select_color; + data['pagination_unselect_color'] = this.pagination_unselect_color; + if (this.index_carousel_list != null) { + data['index_carousel_list'] = this.index_carousel_list.map((v) => v.toJson()).toList(); + } + if (this.pagination_options != null) { + data['pagination_options'] = this.pagination_options; + } + return data; } - return data; - } } -class HomeSlideBannerModelItems { - String modId; - String modPid; - String modName; - String position; - String title; - String subtitle; - String url; - String margin; - String aspectRatio; - String icon; - String img; - String fontColor; - String bgImg; - String bgColor; - String bgColorT; - String badge; - String path; - String data; - String sort; - String isGlobal; +class IndexCarousel { + String img; + String skip_identifier; - HomeSlideBannerModelItems( - {this.modId, - this.modPid, - this.modName, - this.position, - this.title, - this.subtitle, - this.url, - this.margin, - this.aspectRatio, - this.icon, - this.img, - this.fontColor, - this.bgImg, - this.bgColor, - this.bgColorT, - this.badge, - this.path, - this.data, - this.sort, - this.isGlobal}); + IndexCarousel({this.img, this.skip_identifier}); - HomeSlideBannerModelItems.fromJson(Map json) { - modId = json['mod_id']?.toString(); - modPid = json['mod_pid']?.toString(); - modName = json['mod_name']?.toString(); - position = json['position']?.toString(); - title = json['title']?.toString(); - subtitle = json['subtitle']?.toString(); - url = json['url']?.toString(); - margin = json['margin']?.toString(); - aspectRatio = json['aspect_ratio']?.toString(); - icon = json['icon']?.toString(); - img = json['img']?.toString(); - fontColor = json['font_color']?.toString(); - bgImg = json['bg_img']?.toString(); - bgColor = json['bg_color']?.toString(); - bgColorT = json['bg_color_t']?.toString(); - badge = json['badge']?.toString(); - path = json['path']?.toString(); - data = json['data']?.toString(); - sort = json['sort']?.toString(); - isGlobal = json['is_global']?.toString(); - } + factory IndexCarousel.fromJson(Map json) { + return IndexCarousel( + img: json['img'], + skip_identifier: json['skip_identifier'], + ); + } - Map toJson() { - final Map data = new Map(); - data['mod_id'] = this.modId; - data['mod_pid'] = this.modPid; - data['mod_name'] = this.modName; - data['position'] = this.position; - data['title'] = this.title; - data['subtitle'] = this.subtitle; - data['url'] = this.url; - data['margin'] = this.margin; - data['aspect_ratio'] = this.aspectRatio; - data['icon'] = this.icon; - data['img'] = this.img; - data['font_color'] = this.fontColor; - data['bg_img'] = this.bgImg; - data['bg_color'] = this.bgColor; - data['bg_color_t'] = this.bgColorT; - data['badge'] = this.badge; - data['path'] = this.path; - data['data'] = this.data; - data['sort'] = this.sort; - data['is_global'] = this.isGlobal; - return data; - } -} + Map toJson() { + final Map data = new Map(); + data['img'] = this.img; + data['skip_identifier'] = this.skip_identifier; + return data; + } +} \ No newline at end of file diff --git a/lib/widgets/home/home_sreach/home_sreach_creater.dart b/lib/widgets/home/home_sreach/home_sreach_creater.dart new file mode 100644 index 0000000..6102cbd --- /dev/null +++ b/lib/widgets/home/home_sreach/home_sreach_creater.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; +import 'package:zhiying_base_widget/widgets/home/home_sreach/home_sreach_widget.dart'; +import 'package:zhiying_base_widget/widgets/others/normal_nav/normal_nav.dart'; +import 'package:zhiying_comm/zhiying_comm.dart'; + +class HomeSreachCreater extends WidgetCreater { + @override + List createWidgets(Map model) { + return [ + SliverPersistentHeader( + pinned: false, + floating: false, + delegate: HomeSreachDeleagater(), + ), + ]; + } + + @override + bool isSliverChild() { + return true; + } +} diff --git a/lib/widgets/home/home_sreach/home_sreach_sk.dart b/lib/widgets/home/home_sreach/home_sreach_sk.dart new file mode 100644 index 0000000..e3748aa --- /dev/null +++ b/lib/widgets/home/home_sreach/home_sreach_sk.dart @@ -0,0 +1,21 @@ +import 'package:shimmer/shimmer.dart'; +import 'package:flutter/material.dart'; + +class HomeSreachSkeleton extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Container(); + } + + Widget _shimmerWidget({double width, double height, double radius = 0}) { + return Shimmer.fromColors( + baseColor: Colors.grey[300], + highlightColor: Colors.grey[100], + child: Container( + width: width, + height: height, + decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(radius)), + ), + ); + } +} diff --git a/lib/widgets/home/home_sreach/home_sreach_widget.dart b/lib/widgets/home/home_sreach/home_sreach_widget.dart new file mode 100644 index 0000000..c99d7d8 --- /dev/null +++ b/lib/widgets/home/home_sreach/home_sreach_widget.dart @@ -0,0 +1,119 @@ +import 'package:zhiying_comm/zhiying_comm.dart'; +import 'package:flutter/material.dart'; +import 'dart:ui'; + +class HomeSreachDeleagater extends SliverPersistentHeaderDelegate{ + + double _height; + + HomeSreachDeleagater() : super() { + _height = MediaQueryData.fromWindow(window).padding.top + 44; + } + + @override + Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { + print('${shrinkOffset.toString()}'); + double percent = shrinkOffset / _height; + print('${percent.toString()}'); + return HomeSreachWidget(null); + } + + @override + double get maxExtent => _height; + + @override + double get minExtent => _height; + + @override + bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) { + return false; + } + +} + + +/// +/// 首页搜索框 +/// +class HomeSreachWidget extends StatelessWidget { + final Map model; + + const HomeSreachWidget(this.model); + + @override + Widget build(BuildContext context) { + return HomeSreachContainer(); + } +} + +class HomeSreachContainer extends StatefulWidget { + @override + _HomeSreachContainerState createState() => _HomeSreachContainerState(); +} + +class _HomeSreachContainerState extends State { + @override + Widget build(BuildContext context) { + return _getMainWidget(); + } + + /// 主视图 + Widget _getMainWidget() { + return Container( + color: Colors.transparent, + height: 30, + width: double.infinity, + margin: EdgeInsets.only(left: 12.5, right: 12.5, bottom: 10, top: MediaQueryData.fromWindow(window).padding.top + 10), + child: Row( + children: [ + /// 搜索框 + Expanded( + child: _getSreachWidget(), + ), + const SizedBox(width: 10), + + /// 消息 + _getMessageWidget(), + ], + ), + ); + } + + /// 搜索栏 + Widget _getSreachWidget() { + return Container( + height: 30, + width: double.infinity, + child: TextField( + autofocus: false, + style: TextStyle(color: HexColor.fromHex('#FFFFFF'), fontSize: 14), + readOnly: true, + decoration: InputDecoration( + hintText: '输入搜索内容,领券省钱', + hintStyle: TextStyle( + color: Colors.white, + fontSize: 14, + ), + contentPadding: EdgeInsets.zero, + prefixIcon: Icon( + Icons.search, + color: Colors.white, + ), + filled: true, + fillColor: Color(0x50cccccc), + focusedBorder: OutlineInputBorder(borderSide: BorderSide(color: Color(0x00000000)), borderRadius: BorderRadius.all(Radius.circular(30))), + enabledBorder: OutlineInputBorder(borderSide: BorderSide(color: Color(0x00000000)), borderRadius: BorderRadius.all(Radius.circular(30))), + ), + ), + ); + } + + /// 消息widget + Widget _getMessageWidget() { + return Container( + width: 30, + height: 30, + color: Colors.red, + ); + } +}