diff --git a/.fvm/flutter_sdk b/.fvm/flutter_sdk index 143ba8c..18fa846 120000 --- a/.fvm/flutter_sdk +++ b/.fvm/flutter_sdk @@ -1 +1 @@ -/Users/stay/fvm/versions/3.19.2 \ No newline at end of file +/Users/biao/fvm/versions/3.19.2 \ No newline at end of file diff --git a/.fvm/fvm_config.json b/.fvm/fvm_config.json index 7691f75..e4dbfda 100644 --- a/.fvm/fvm_config.json +++ b/.fvm/fvm_config.json @@ -1,4 +1,3 @@ { - "flutterSdkVersion": "3.19.2", - "flavors": {} + "flutterSdkVersion": "3.19.2" } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8a15cae --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dart.flutterSdkPath": ".fvm/versions/3.19.2" +} \ No newline at end of file diff --git a/assets/images/micro_phone.gif b/assets/images/micro_phone.gif index d1da100..6aa5b32 100644 --- a/assets/images/micro_phone.gif +++ b/assets/images/micro_phone.gif diff --git a/assets/images/read_background.png b/assets/images/read_background.png new file mode 100644 index 0000000..05e66c4 --- /dev/null +++ b/assets/images/read_background.png diff --git a/assets/images/reade_answer.gif b/assets/images/reade_answer.gif index 39f882f..be6c871 100644 --- a/assets/images/reade_answer.gif +++ b/assets/images/reade_answer.gif diff --git a/assets/images/shop_desc.png b/assets/images/shop_desc.png new file mode 100644 index 0000000..5d3de69 --- /dev/null +++ b/assets/images/shop_desc.png diff --git a/assets/images/voice.png b/assets/images/voice.png index 01a1aed..d186c69 100644 --- a/assets/images/voice.png +++ b/assets/images/voice.png diff --git a/assets/images/xe_shop.png b/assets/images/xe_shop.png new file mode 100644 index 0000000..28d040d --- /dev/null +++ b/assets/images/xe_shop.png diff --git a/assets/sounds/class_time.mp3 b/assets/sounds/class_time.mp3 new file mode 100644 index 0000000..3b5226e --- /dev/null +++ b/assets/sounds/class_time.mp3 diff --git a/assets/sounds/count_with_me_instrumental.mp3 b/assets/sounds/count_with_me_instrumental.mp3 new file mode 100644 index 0000000..483dbc8 --- /dev/null +++ b/assets/sounds/count_with_me_instrumental.mp3 diff --git a/assets/sounds/game_time.mp3 b/assets/sounds/game_time.mp3 new file mode 100644 index 0000000..e0b4391 --- /dev/null +++ b/assets/sounds/game_time.mp3 diff --git a/assets/sounds/in_my_tummy_instrumental.mp3 b/assets/sounds/in_my_tummy_instrumental.mp3 new file mode 100644 index 0000000..0edc830 --- /dev/null +++ b/assets/sounds/in_my_tummy_instrumental.mp3 diff --git a/assets/sounds/music_time.mp3 b/assets/sounds/music_time.mp3 new file mode 100644 index 0000000..bf94df1 --- /dev/null +++ b/assets/sounds/music_time.mp3 diff --git a/assets/sounds/quiz_time.mp3 b/assets/sounds/quiz_time.mp3 new file mode 100644 index 0000000..026a2ee --- /dev/null +++ b/assets/sounds/quiz_time.mp3 diff --git a/assets/sounds/reading_time.mp3 b/assets/sounds/reading_time.mp3 new file mode 100644 index 0000000..3fa3f2b --- /dev/null +++ b/assets/sounds/reading_time.mp3 diff --git a/assets/sounds/touch_instrumental.mp3 b/assets/sounds/touch_instrumental.mp3 new file mode 100644 index 0000000..5b224cc --- /dev/null +++ b/assets/sounds/touch_instrumental.mp3 diff --git a/assets/sounds/video_time.mp3 b/assets/sounds/video_time.mp3 new file mode 100644 index 0000000..4837f74 --- /dev/null +++ b/assets/sounds/video_time.mp3 diff --git a/assets/sounds/welcome_to_wow.mp3 b/assets/sounds/welcome_to_wow.mp3 new file mode 100644 index 0000000..fb6c66d --- /dev/null +++ b/assets/sounds/welcome_to_wow.mp3 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ed3741a..45bb20f 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -2327,7 +2327,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 16; DEVELOPMENT_TEAM = T8P9KW8GWH; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; @@ -2671,7 +2671,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 16; DEVELOPMENT_TEAM = T8P9KW8GWH; ENABLE_BITCODE = NO; HEADER_SEARCH_PATHS = ( @@ -2876,7 +2876,7 @@ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 13; + CURRENT_PROJECT_VERSION = 16; DEVELOPMENT_TEAM = T8P9KW8GWH; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index b7b9e34..493f0d2 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -77,11 +77,11 @@ NSCameraUsageDescription - 需要访问相机完成拍照 + 需要访问相机,完成修改头像功能 NSMicrophoneUsageDescription - 需要获取录音完成后续功能 + 需要获取录音,完成上课功能 NSPhotoLibraryUsageDescription - 需要获取照片用来修改头像 + 需要获取照片,完成上传头像功能 UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName diff --git a/lib/common/core/app_consts.dart b/lib/common/core/app_consts.dart index 39c8fb2..e5902c4 100644 --- a/lib/common/core/app_consts.dart +++ b/lib/common/core/app_consts.dart @@ -2,16 +2,26 @@ import '../request/basic_config.dart'; class AppConsts { /// 隐私协议 - static const String userPrivacyPolicyUrl = 'http://page.kouyuxingqiu.com/wowenglishuserregister.html'; + static const String userPrivacyPolicyUrl = + 'http://page.kouyuxingqiu.com/wowenglishuserregister.html'; /// 儿童隐私协议 - static const String childrenPrivacyPolicyUrl = 'http://page.kouyuxingqiu.com/wowenglishchildprotect.html'; + static const String childrenPrivacyPolicyUrl = + 'http://page.kouyuxingqiu.com/wowenglishchildprotect.html'; + + /// 小鵝通 + static const String xiaoeShopUrl = 'https://appo61s7g678876.h5.xiaoeknow.com'; /// 与第三方共享协议 - static const String userTermSdkUrl = 'http://page.kouyuxingqiu.com/term_sdk.html'; + static const String userTermSdkUrl = + 'http://page.kouyuxingqiu.com/term_sdk.html'; /// 先声SDK - static String xsAppKey = 'a418'; - static String xsAppSecretKey = BasicConfig.isTestDev?'1a16f31f2611bf32fb7b3fc38f5b2c81':'c11163aa6c834a028da4a4b30955be99'; - static String xsAppService = BasicConfig.isTestDev?'ws://trial.cloud.ssapi.cn:8080':'"wss://api.cloud.ssapi.cn'; + static String xsAppKey = 'a418'; + static String xsAppSecretKey = BasicConfig.isTestDev + ? '1a16f31f2611bf32fb7b3fc38f5b2c81' + : 'c11163aa6c834a028da4a4b30955be99'; + static String xsAppService = BasicConfig.isTestDev + ? 'ws://trial.cloud.ssapi.cn:8080' + : '"wss://api.cloud.ssapi.cn'; } diff --git a/lib/pages/games/bloc.dart b/lib/pages/games/bloc.dart index a118794..632fc9a 100644 --- a/lib/pages/games/bloc.dart +++ b/lib/pages/games/bloc.dart @@ -2,17 +2,17 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import 'event.dart'; import 'game_entity.dart'; import 'state.dart'; class GamesBloc extends Bloc { - late MethodChannel _methodChannel; //手动初始化4个GameEntity对象 - final List _games = [ + final List _games = [ GameEntity() ..id = 1 ..imageName = 'game_food_1'.assetPng @@ -31,7 +31,7 @@ class GamesBloc extends Bloc { ..name = 'Animal' ]; - List get listData => _games; + List get listData => _games; GamesBloc() : super(GamesState().init()) { on(_init); @@ -39,13 +39,16 @@ class GamesBloc extends Bloc { } void _init(InitEvent event, Emitter emit) async { + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.inMyTummy); emit(state.clone()); } void _gotoGamePage(GotoGamePageEvent event, Emitter emit) async { + await AudioPlayerUtil.getInstance().pause(); try { _methodChannel = const MethodChannel('wow_english/game_method_channel'); - await _methodChannel.invokeMethod('openGamePage', { "gameId": event.gameId }); + await _methodChannel + .invokeMethod('openGamePage', {"gameId": event.gameId}); } on PlatformException catch (e) { debugPrint("Failed to go to native page: '${e.message}'."); } diff --git a/lib/pages/home/bloc.dart b/lib/pages/home/bloc.dart index eaf337d..6cf5a7c 100644 --- a/lib/pages/home/bloc.dart +++ b/lib/pages/home/bloc.dart @@ -1,4 +1,8 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:bloc/bloc.dart'; +import 'package:wow_english/common/core/user_util.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import '../../common/core/app_config_helper.dart'; import '../../common/request/dao/system_dao.dart'; @@ -16,6 +20,9 @@ class HomeBloc extends Bloc { bool exchangeResult = false; void _init(InitEvent event, Emitter emit) async { + if (UserUtil.isLogined()) { + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.welcomeToWow); + } await _checkUpdate(emit); } @@ -37,7 +44,7 @@ class HomeBloc extends Bloc { return; } Log.d( - "WQF _checkUpdate appVersionEntity: $appVersionEntity localVersion=$localVersion"); + "HomeBloc _checkUpdate appVersionEntity: $appVersionEntity localVersion=$localVersion"); if (localVersion < int.parse(appVersionEntity.version ?? '0')) { emit(UpdateDialogState( appVersionEntity.volType == UpdateStrategy.FORCE.name, diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 09c344c..c1d86b7 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -1,15 +1,18 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app_update/azhon_app_update.dart'; import 'package:flutter_app_update/update_model.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:wow_english/common/core/app_config_helper.dart'; +import 'package:wow_english/common/core/app_consts.dart'; import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/models/app_version_entity.dart'; import 'package:wow_english/pages/home/state.dart'; import 'package:wow_english/pages/home/widgets/BaseHomeHeaderWidget.dart'; import 'package:wow_english/pages/shop/exchane/bloc/exchange_lesson_bloc.dart'; import 'package:wow_english/pages/user/bloc/user_bloc.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import '../../common/core/user_util.dart'; import '../../common/dialogs/show_dialog.dart'; @@ -57,7 +60,9 @@ class _HomePageView extends StatelessWidget { child: Column( children: [ BaseHomeHeaderWidget( - callBack: (value) => { + callBack: (value) async => { + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.touch), bloc.exchangeResult = value['exchange'], bloc.add(ExchangeSuccessEvent()) }), @@ -68,7 +73,9 @@ class _HomePageView extends StatelessWidget { Expanded( child: GestureDetector( onTap: () { - _checkPermission(() { + _checkPermission(() async { + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.classTime); pushNamed(AppRouteName.courseUnit) .then((value) => { if (value != null) @@ -102,13 +109,45 @@ class _HomePageView extends StatelessWidget { ), ), ), + BlocBuilder( + builder: (context, userState) { + return GestureDetector( + onTap: () { + _checkPermission(() async { + await AudioPlayerUtil.getInstance().pause(); + Navigator.of(context).pushNamed( + AppRouteName.webView, + arguments: { + 'urlStr': AppConsts.xiaoeShopUrl, + 'webViewTitle': 'Wow精选' + }).then((value) async => { + await AudioPlayerUtil.getInstance().playAudio( + AudioPlayerUtilType.touch), + }); + }, bloc); + }, + child: Offstage( + offstage: AppConfigHelper.shouldHidePay() || + !UserUtil.isLogined(), + child: Image.asset('xe_shop'.assetPng, + width: 140.5.w, height: 172.h), + )); + }), Expanded( child: BlocBuilder( builder: (context, userState) { return GestureDetector( onTap: () { - _checkPermission(() { - pushNamed(AppRouteName.games); + _checkPermission(() async { + await AudioPlayerUtil.getInstance() + .playAudio( + AudioPlayerUtilType.gameTime); + pushNamed(AppRouteName.games) + .then((value) => { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType + .touch), + }); }, bloc); }, child: Column( @@ -157,6 +196,8 @@ class _HomePageView extends StatelessWidget { }, rightTap: () { popPage(); pushNamed(AppRouteName.shop).then((value) { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.touch); if (value != null) { bloc.exchangeResult = value['exchange']; bloc.add(ExchangeSuccessEvent()); diff --git a/lib/pages/home/widgets/BaseHomeHeaderWidget.dart b/lib/pages/home/widgets/BaseHomeHeaderWidget.dart index 4866201..0db3d8e 100644 --- a/lib/pages/home/widgets/BaseHomeHeaderWidget.dart +++ b/lib/pages/home/widgets/BaseHomeHeaderWidget.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:wow_english/common/core/app_config_helper.dart'; import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import '../../../common/core/user_util.dart'; import '../../../models/course_entity.dart'; @@ -83,7 +84,8 @@ class BaseHomeHeaderWidget extends StatelessWidget { offstage: AppConfigHelper.shouldHidePay() || !UserUtil.isLogined(), child: GestureDetector( - onTap: () => { + onTap: () async => { + await AudioPlayerUtil.getInstance().pause(), pushNamed(AppRouteName.shop).then((value) { if (value != null) { if (callBack == null) { @@ -113,8 +115,9 @@ class BaseHomeHeaderWidget extends StatelessWidget { ); } - void onUserClick() { + Future onUserClick() async { if (UserUtil.isLogined()) { + await AudioPlayerUtil.getInstance().pause(); pushNamed(AppRouteName.user).then((value) { if (value != null) { if (callBack == null) { diff --git a/lib/pages/practice/topic_picture_page.dart b/lib/pages/practice/topic_picture_page.dart index 7de6e92..274e5a0 100644 --- a/lib/pages/practice/topic_picture_page.dart +++ b/lib/pages/practice/topic_picture_page.dart @@ -59,7 +59,12 @@ class _TopicPicturePage extends StatelessWidget { builder: (context, state) { final bloc = BlocProvider.of(context); return Container( - color: Colors.white, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('read_background'.assetPng), // 背景图片路径 + fit: BoxFit.cover, // 适应图片的方式 + ), + ), child: Stack( children: [ Column( @@ -75,6 +80,7 @@ class _TopicPicturePage extends StatelessWidget { // Navigator.pop(context); }, ), + 35.verticalSpace, Expanded( child: PageView.builder( itemCount: bloc.entity?.topics?.length, @@ -109,12 +115,7 @@ class _TopicPicturePage extends StatelessWidget { }), ) ], - ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Image.asset('bottom_grass'.assetPng)) + ) ], ), ); @@ -299,7 +300,7 @@ class _TopicPicturePage extends StatelessWidget { 26.verticalSpace, SizedBox( height: 143.h, - width: 163.w * (topics?.topicAnswerList?.length ?? 0), + width: 203.w * (topics?.topicAnswerList?.length ?? 0), child: ListView.builder( scrollDirection: Axis.horizontal, physics: const NeverScrollableScrollPhysics(), @@ -321,7 +322,7 @@ class _TopicPicturePage extends StatelessWidget { builder: (context, state) { final bloc = BlocProvider.of(context); return Container( - padding: EdgeInsets.symmetric(horizontal: 10.w), + padding: EdgeInsets.symmetric(horizontal: 20.w), child: GestureDetector( onTap: () => bloc.add(SelectItemEvent(index)), child: Container( @@ -333,15 +334,13 @@ class _TopicPicturePage extends StatelessWidget { borderRadius: BorderRadius.circular(15), ), height: 143.h, - width: 143.w, + width: 163.w, child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(15), - border: Border.all( - width: 1.0, color: const Color(0xFF140C10)), image: DecorationImage( - fit: BoxFit.fitWidth, + fit: BoxFit.fill, image: NetworkImage(answerList?.picUrl ?? ''))), ), ), @@ -449,12 +448,18 @@ class _TopicPicturePage extends StatelessWidget { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - OwImageWidget( - name: topics?.picUrl ?? '', - height: 186.h, - width: 186.w, + ClipRRect( + borderRadius: BorderRadius.circular(20), + child: Container( + color: Colors.white, + child: OwImageWidget( + name: topics?.picUrl ?? '', + height: 186.h, + width: 186.w, + ), + ), ), - 160.horizontalSpace, + 120.horizontalSpace, Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -472,8 +477,8 @@ class _TopicPicturePage extends StatelessWidget { bloc.voicePlayState == VoicePlayState.playing ? 'reade_answer'.assetGif : 'voice'.assetPng, - height: 52.h, - width: 46.w, + height: 45.h, + width: 45.w, ), 10.horizontalSpace, Text(topics?.word ?? '') @@ -506,8 +511,8 @@ class _TopicPicturePage extends StatelessWidget { bloc.isVoicing ? 'micro_phone'.assetGif : 'micro_phone'.assetPng, - height: 75.w, - width: 75.w, + height: 46.h, + width: 46.w, ), ) ], diff --git a/lib/pages/practice/widgets/practice_header_widget.dart b/lib/pages/practice/widgets/practice_header_widget.dart index 493977a..635d0a0 100644 --- a/lib/pages/practice/widgets/practice_header_widget.dart +++ b/lib/pages/practice/widgets/practice_header_widget.dart @@ -12,9 +12,10 @@ class PracticeHeaderWidget extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - color: Colors.white, + color: Colors.transparent, height: kToolbarHeight + 3.h, child: AppBar( + backgroundColor: Colors.transparent, leading: GestureDetector( child: Image.asset( 'back_around'.assetPng, @@ -25,7 +26,7 @@ class PracticeHeaderWidget extends StatelessWidget { ), centerTitle: true, title: Container( - height: 40.h, + height: 20.h, width: 100.w, // 容器宽度 // padding: EdgeInsets.symmetric(horizontal: 27.w, vertical: 10.h), alignment: Alignment.center, diff --git a/lib/pages/reading/bloc/reading_event.dart b/lib/pages/reading/bloc/reading_event.dart index 9d4ae4d..2adfac8 100644 --- a/lib/pages/reading/bloc/reading_event.dart +++ b/lib/pages/reading/bloc/reading_event.dart @@ -39,7 +39,7 @@ class XSVoiceStartEvent extends ReadingPageEvent { final String content; final String type; final String? userId; - XSVoiceStartEvent(this.content,this.type,this.userId); + XSVoiceStartEvent(this.content, this.type, this.userId); } ///先声评测停止 @@ -52,4 +52,7 @@ class OnXSVoiceStateChangeEvent extends ReadingPageEvent {} class VoicePlayStateChangeEvent extends ReadingPageEvent {} ///录音播放 -class PlayRecordAudioEvent extends ReadingPageEvent {} \ No newline at end of file +class PlayRecordAudioEvent extends ReadingPageEvent {} + +///播放下一页 +class PlayNextPageEvent extends ReadingPageEvent {} diff --git a/lib/pages/reading/reading_page.dart b/lib/pages/reading/reading_page.dart index 4b6f27d..cf13c4b 100644 --- a/lib/pages/reading/reading_page.dart +++ b/lib/pages/reading/reading_page.dart @@ -21,17 +21,16 @@ class ReadingPage extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => ReadingPageBloc(context, PageController(), courseLessonId ?? '') - ..add(InitBlocEvent()) - ..add(RequestDataEvent()) - ..add(XSVoiceInitEvent( - { - 'appKey':AppConsts.xsAppKey, - 'service':AppConsts.xsAppService, - 'secretKey':AppConsts.xsAppSecretKey, - 'userId':UserUtil.getUser()!.id.toString(), - } - )), + create: (_) => + ReadingPageBloc(context, PageController(), courseLessonId ?? '') + ..add(InitBlocEvent()) + ..add(RequestDataEvent()) + ..add(XSVoiceInitEvent({ + 'appKey': AppConsts.xsAppKey, + 'service': AppConsts.xsAppService, + 'secretKey': AppConsts.xsAppSecretKey, + 'userId': UserUtil.getUser()!.id.toString(), + })), child: _ReadingPage(), ); } @@ -60,8 +59,8 @@ class _ReadingPage extends StatelessWidget { ); } - Widget _readingPageView() => BlocBuilder( - builder: (context, state) { + Widget _readingPageView() => + BlocBuilder(builder: (context, state) { final bloc = BlocProvider.of(context); return Container( color: Colors.white, @@ -84,16 +83,14 @@ class _ReadingPage extends StatelessWidget { children: [ Padding( padding: - EdgeInsets.only(left: ScreenUtil().bottomBarHeight), + EdgeInsets.only(left: ScreenUtil().bottomBarHeight), child: IconButton( onPressed: () { - popPage( - data:{ - 'currentStep':bloc.currentPage, - 'courseLessonId':bloc.courseLessonId, - 'isCompleted':bloc.isLastPage(), - } - ); + popPage(data: { + 'currentStep': bloc.currentPage, + 'courseLessonId': bloc.courseLessonId, + 'isCompleted': bloc.isLastPage(), + }); }, icon: Image.asset( 'back_around'.assetPng, @@ -158,6 +155,7 @@ class _ReadingPage extends StatelessWidget { margin: EdgeInsets.symmetric(horizontal: 10.w), child: Row( children: [ + 5.horizontalSpace, GestureDetector( onTap: () { if (bloc.isRecording) { @@ -167,7 +165,7 @@ class _ReadingPage extends StatelessWidget { }, child: Image.asset( bloc.voicePlayState == VoicePlayState.playing && - bloc.isOriginAudioPlaying + bloc.isOriginAudioPlaying ? 'reade_answer'.assetGif : 'voice'.assetPng, height: 40.h, @@ -179,12 +177,12 @@ class _ReadingPage extends StatelessWidget { ), Expanded( child: Text( - bloc.currentPageData()?.word?.trim() ?? '', - style: TextStyle( - color: const Color(0xFF333333), fontSize: 21.sp), - maxLines: 2, - overflow: TextOverflow.ellipsis, - )), + bloc.currentPageData()?.word?.trim() ?? '', + style: TextStyle( + color: const Color(0xFF333333), fontSize: 21.sp), + maxLines: 2, + overflow: TextOverflow.ellipsis, + )), SizedBox( width: 10.w, ), @@ -241,8 +239,7 @@ class _ReadingPage extends StatelessWidget { return Stack( children: [ Positioned.fill( - child: - Image.network(readings.picUrl ?? '', fit: BoxFit.cover), + child: Image.network(readings.picUrl ?? '', fit: BoxFit.cover), ), ], ); diff --git a/lib/pages/section/bloc/section_bloc.dart b/lib/pages/section/bloc/section_bloc.dart index 0c1d94c..4df2807 100644 --- a/lib/pages/section/bloc/section_bloc.dart +++ b/lib/pages/section/bloc/section_bloc.dart @@ -1,12 +1,15 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; import 'package:wow_english/common/request/dao/lesson_dao.dart'; import 'package:wow_english/common/request/exception.dart'; import 'package:wow_english/common/request/dao/listen_dao.dart'; import 'package:wow_english/models/course_process_entity.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import 'package:wow_english/utils/loading.dart'; import 'package:wow_english/utils/toast_util.dart'; @@ -61,16 +64,20 @@ class SectionBloc extends Bloc { on(_requestEnterClass); on(_requestVideoLesson); on(_pageControllerChange); + on((event, emit) async { + await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe); + }); } void _requestSectionsData( RequestDataEvent event, Emitter emitter) async { try { await loading(() async { - List? courseSectionEntities = await LessonDao.courseSection(courseUnitId: event.courseUnitId); + List? courseSectionEntities = + await LessonDao.courseSection(courseUnitId: event.courseUnitId); if (courseSectionEntities != null) { _courseSectionDatasMap[event.courseUnitId] = - await LessonDao.courseSection(courseUnitId: event.courseUnitId); + await LessonDao.courseSection(courseUnitId: event.courseUnitId); emitter(LessonDataLoadState()); } }); @@ -224,7 +231,8 @@ class SectionBloc extends Bloc { ///查找当前unit的下一个section CourseSectionEntity? nextCourseSectionEntity = findCourseSectionBySort(curSectionSort + 1); - return checkCourseSectionLocked(courseLessonId, nextCourseSectionEntity, emitter); + return checkCourseSectionLocked( + courseLessonId, nextCourseSectionEntity, emitter); } catch (e) { if (e is ApiException) { showToast(e.message.toString()); @@ -234,7 +242,9 @@ class SectionBloc extends Bloc { } ///检查section是否锁定 - Future checkCourseSectionLocked(int courseLessonId, CourseSectionEntity? courseSectionEntity, + Future checkCourseSectionLocked( + int courseLessonId, + CourseSectionEntity? courseSectionEntity, Emitter emitter) async { if (courseSectionEntity != null) { if (courseSectionEntity.lock == false) { @@ -243,15 +253,15 @@ class SectionBloc extends Bloc { } else { ///如果section锁了,请求当前unit下的section数据,查询解锁状态 int courseUnitId = courseSectionEntity.courseUnitId; - CourseSectionEntity? result = await loading(() async { + CourseSectionEntity? result = await loading(() async { List? tempSectionEntities = - await LessonDao.courseSection(courseUnitId: courseUnitId); + await LessonDao.courseSection(courseUnitId: courseUnitId); if (tempSectionEntities != null) { _courseSectionDatasMap[courseUnitId] = tempSectionEntities; emitter(LessonDataLoadState()); } courseSectionEntity = tempSectionEntities?.firstWhereOrNull( - (element) => element.id == courseSectionEntity?.id); + (element) => element.id == courseSectionEntity?.id); if (courseSectionEntity?.lock == false) { ///刷新后的数据如果解锁了,直接返回 return courseSectionEntity; @@ -270,18 +280,20 @@ class SectionBloc extends Bloc { if (curCourseUnitDetail != null) { ///再根据当前unit的sortOrder找出下一个unit CourseUnitDetail? nextCourseUnitDetail = - _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) => - element.sortOrder == (curCourseUnitDetail.sortOrder! + 1)); + _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) => + element.sortOrder == (curCourseUnitDetail.sortOrder! + 1)); if (nextCourseUnitDetail != null) { if (nextCourseUnitDetail.lock == true) { ///如果下一个unit是锁定状态,请求数据刷新查询解锁状态 CourseSectionEntity? result = await loading(() async { - CourseUnitEntity? newCourseUnitEntity = await LessonDao.courseUnit( - _courseUnitEntity.nowCourseModuleId); + CourseUnitEntity? newCourseUnitEntity = + await LessonDao.courseUnit( + _courseUnitEntity.nowCourseModuleId); ///拿到重新获取到的unit后,再次判断是否解锁 - nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList?.firstWhereOrNull( + nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList + ?.firstWhereOrNull( (element) => element.id == nextCourseUnitDetail?.id); if (nextCourseUnitDetail?.lock == false) { ///解锁状态从锁定到解锁,覆盖原unit数据并刷新ui @@ -289,7 +301,8 @@ class SectionBloc extends Bloc { courseUnitEntityChanged = true; emitter(LessonDataLoadState()); - return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail!.id!, emitter); + return checkCourseSectionLockedOfNextUnit( + courseLessonId, nextCourseUnitDetail!.id!, emitter); } else { showToast('下个单元课程还没解锁哦'); @@ -299,10 +312,12 @@ class SectionBloc extends Bloc { }); return result; } else { - return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail.id!, emitter); + return checkCourseSectionLockedOfNextUnit( + courseLessonId, nextCourseUnitDetail.id!, emitter); } } else { showToast("恭喜你,本阶段学到顶啦"); + ///最后一个unit了 return null; } @@ -314,13 +329,16 @@ class SectionBloc extends Bloc { } ///检查下一个unit的(第一个)section - Future checkCourseSectionLockedOfNextUnit(int courseLessonId, int nextCourseUnitDetailId, + Future checkCourseSectionLockedOfNextUnit( + int courseLessonId, + int nextCourseUnitDetailId, Emitter emitter) async { - CourseSectionEntity? firstSectionNextUnit = await getFirstSectionByUnitId( - nextCourseUnitDetailId, emitter); + CourseSectionEntity? firstSectionNextUnit = + await getFirstSectionByUnitId(nextCourseUnitDetailId, emitter); if (firstSectionNextUnit != null) { ///下个unit的第一个section如果不为空,再次检查是否锁定 - CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked(courseLessonId, firstSectionNextUnit, emitter); + CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked( + courseLessonId, firstSectionNextUnit, emitter); if (courseSectionEntity != null) { ///只有是下一unit的第一个section并且解锁了,才跳转 _pageController.nextPage( diff --git a/lib/pages/section/bloc/section_event.dart b/lib/pages/section/bloc/section_event.dart index e9fa3ad..06eb1fb 100644 --- a/lib/pages/section/bloc/section_event.dart +++ b/lib/pages/section/bloc/section_event.dart @@ -9,6 +9,8 @@ class RequestDataEvent extends SectionEvent { RequestDataEvent(this.courseUnitId); } +class InitEvent extends SectionEvent {} + ///获取视频课程内容 class RequestVideoLessonEvent extends SectionEvent { final String courseLessonId; diff --git a/lib/pages/section/section_page.dart b/lib/pages/section/section_page.dart index 8adbc11..2a31395 100644 --- a/lib/pages/section/section_page.dart +++ b/lib/pages/section/section_page.dart @@ -1,3 +1,4 @@ +import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -11,6 +12,8 @@ import 'package:wow_english/pages/section/widgets/section_item.dart'; import 'package:wow_english/pages/section/widgets/section_bouns_item.dart'; import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; import 'package:wow_english/route/route.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; +import 'package:wow_english/utils/log_util.dart'; import 'package:wow_english/utils/toast_util.dart'; import '../../models/course_section_entity.dart'; @@ -38,7 +41,8 @@ class SectionPage extends StatelessWidget { initialPage, PageController(initialPage: initialPage), ScrollController(), - ScrollController()), + ScrollController()) + ..add(InitEvent()), //为了触发指示器进入后计算位置 // ..add(CurrentUnitIndexChangeEvent(initialPage)), child: _SectionPageView(context), @@ -55,7 +59,7 @@ class _SectionPageView extends StatelessWidget { Widget build(BuildContext context) { final bloc = BlocProvider.of(context); return BlocListener( - listener: (context, state) { + listener: (context, state) async { if (state is RequestVideoLessonState) { final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; var title = ''; @@ -87,6 +91,8 @@ class _SectionPageView extends StatelessWidget { currentTime: dataMap['currentTime'], autoNextSection: dataMap['nextSection'])); } + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.countWithMe); }); return; } @@ -96,12 +102,22 @@ class _SectionPageView extends StatelessWidget { state.courseType != SectionType.pictureBook.value) { ///视频类型 ///获取视频课程内容 + if (state.courseType == 1) { + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.musicTime); + } else { + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.videoTime); + } bloc.add(RequestVideoLessonEvent( state.courseLessonId, state.courseType)); + return; } if (state.courseType == SectionType.pictureBook.value) { + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.readingTime); //绘本 pushNamed(AppRouteName.reading, arguments: {'courseLessonId': state.courseLessonId}) @@ -114,13 +130,18 @@ class _SectionPageView extends StatelessWidget { currentStep: dataMap['currentStep'], autoNextSection: dataMap['nextSection'], )); + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.countWithMe); } }); + return; } if (state.courseType == SectionType.practice.value) { //练习 + await AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.quizTime); pushNamed(AppRouteName.topicPic, arguments: {'courseLessonId': state.courseLessonId}) .then((value) { @@ -131,6 +152,8 @@ class _SectionPageView extends StatelessWidget { currentStep: dataMap['currentStep'], autoNextSection: dataMap['nextSection'])); } + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.countWithMe); }); return; } diff --git a/lib/pages/shop/home/shop_desc_page.dart b/lib/pages/shop/home/shop_desc_page.dart new file mode 100644 index 0000000..5121b3b --- /dev/null +++ b/lib/pages/shop/home/shop_desc_page.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +import 'package:wow_english/common/extension/string_extension.dart'; +import 'package:wow_english/common/widgets/we_app_bar.dart'; + +///购前须知页 +class ShopDescPage extends StatelessWidget { + const ShopDescPage({super.key}); + + @override + Widget build(BuildContext context) { + return _ShopDescPageView(); + } +} + +class _ShopDescPageView extends StatelessWidget { + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const WEAppBar( + titleText: '购前须知', + centerTitle: true, + ), + body: SingleChildScrollView( + child: Center( + child: Image.asset('shop_desc'.assetPng), + ), + ), + ); + } +} diff --git a/lib/pages/shop/home/shop_home_page.dart b/lib/pages/shop/home/shop_home_page.dart index 481e22e..b33ab4a 100644 --- a/lib/pages/shop/home/shop_home_page.dart +++ b/lib/pages/shop/home/shop_home_page.dart @@ -58,7 +58,7 @@ class _ShopHomeView extends StatelessWidget { ), color: Colors.white, onPressed: () { - showToast('购前须知'); + pushNamed(AppRouteName.shopDesc); }, ) ], diff --git a/lib/pages/unit/bloc.dart b/lib/pages/unit/bloc.dart index 7609fdc..5f45097 100644 --- a/lib/pages/unit/bloc.dart +++ b/lib/pages/unit/bloc.dart @@ -1,5 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import '../../common/request/dao/lesson_dao.dart'; import '../../common/request/exception.dart'; @@ -24,6 +25,9 @@ class UnitBloc extends Bloc { UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) { on(_requestUnitDatas); + on((event, emit) { + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.inMyTummy); + }); } void _requestUnitDatas( @@ -44,16 +48,28 @@ class UnitBloc extends Bloc { return _moduleEntity?.code ?? _unitData?.courseModuleCode; } - void headerActionEvent(HeaderActionType type) { + Future headerActionEvent(HeaderActionType type) async { + await AudioPlayerUtil.getInstance().pause(); if (type == HeaderActionType.video) { + //视频跟读暂时隐藏了 pushNamed(AppRouteName.reAfter); } else if (type == HeaderActionType.phase) { - pushNamed(AppRouteName.courseModule); + pushNamed(AppRouteName.courseModule).then((value) => { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.inMyTummy) + }); + ; } else if (type == HeaderActionType.listen) { - pushNamed(AppRouteName.listen); + pushNamed(AppRouteName.listen).then((value) => { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.inMyTummy) + }); } else if (type == HeaderActionType.shop) { - pushNamed(AppRouteName.shop) - .then((value) => {exchangeResult = value['exchange']}); + pushNamed(AppRouteName.shop).then((value) => { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.inMyTummy), + exchangeResult = value['exchange'] + }); } else if (type == HeaderActionType.user) { pushNamed(AppRouteName.user); } diff --git a/lib/pages/unit/event.dart b/lib/pages/unit/event.dart index 53a8b93..be374dc 100644 --- a/lib/pages/unit/event.dart +++ b/lib/pages/unit/event.dart @@ -6,3 +6,5 @@ class RequestUnitDataEvent extends UnitEvent { RequestUnitDataEvent(this.moduleId); } + +class UnitInitEvent extends UnitEvent {} diff --git a/lib/pages/unit/view.dart b/lib/pages/unit/view.dart index bb3d3f6..78148c7 100644 --- a/lib/pages/unit/view.dart +++ b/lib/pages/unit/view.dart @@ -5,6 +5,7 @@ import 'package:wow_english/pages/unit/state.dart'; import 'package:wow_english/pages/unit/widget/course_unit_item.dart'; import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; import 'package:wow_english/route/route.dart'; +import 'package:wow_english/utils/audio_player_util.dart'; import '../../models/course_module_entity.dart'; import '../../models/course_unit_entity.dart'; @@ -23,6 +24,7 @@ class UnitPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (BuildContext context) => UnitBloc(courseModuleEntity) + ..add(UnitInitEvent()) ..add(RequestUnitDataEvent(courseModuleEntity?.id)), child: Builder(builder: (context) => _buildPage(context)), ); @@ -41,6 +43,8 @@ class UnitPage extends StatelessWidget { HomeTabHeaderWidget( courseModuleCode: bloc.getCourseModuleCode(), onBack: () { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.touch); popPage(data: {'exchange': bloc.exchangeResult}); }, actionTap: (HeaderActionType type) { @@ -58,17 +62,19 @@ class UnitPage extends StatelessWidget { CourseUnitDetail? data = bloc.unitData?.courseUnitVOList?[index]; return GestureDetector( - onTap: () { + onTap: () async { if (data.lock == true) { showToast('当前单元课程暂未解锁'); return; } - + // await AudioPlayerUtil.getInstance().pause(); pushNamed(AppRouteName.courseSection, arguments: { 'courseUnitEntity': bloc.unitData, 'courseUnitId': data.id }).then((value) { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.inMyTummy); if (value != null) { Map dataMap = value as Map; diff --git a/lib/pages/unit/widget/course_unit_item.dart b/lib/pages/unit/widget/course_unit_item.dart index e47a09a..9288fad 100644 --- a/lib/pages/unit/widget/course_unit_item.dart +++ b/lib/pages/unit/widget/course_unit_item.dart @@ -17,14 +17,13 @@ class CourseUnitItem extends StatelessWidget { @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 24.h), - child: Stack( - children: [ - _normalItem(), - _lockWidget(), - ], - ) - ); + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 24.h), + child: Stack( + children: [ + _normalItem(), + _lockWidget(), + ], + )); } Widget _normalItem() { @@ -40,17 +39,17 @@ class CourseUnitItem extends StatelessWidget { children: [ Expanded( child: Container( - decoration: BoxDecoration( - border: Border.all( - width: 2, - color: const Color(0xFF140C10), - ), - borderRadius: BorderRadius.circular(6)), - child: OwImageWidget( - name: unitLesson.coverUrl ?? '', - fit: BoxFit.fitHeight, + decoration: BoxDecoration( + border: Border.all( + width: 2, + color: const Color(0xFF140C10), ), - )), + borderRadius: BorderRadius.circular(6)), + child: OwImageWidget( + name: unitLesson.coverUrl ?? '', + fit: BoxFit.fitHeight, + ), + )), 20.verticalSpace, SizedBox( height: 40.h, @@ -58,8 +57,7 @@ class CourseUnitItem extends StatelessWidget { unitLesson.name ?? '', maxLines: 2, overflow: TextOverflow.ellipsis, - style: - TextStyle(fontSize: 11.sp, color: const Color(0xFF140C10)), + style: TextStyle(fontSize: 11.sp, color: const Color(0xFF140C10)), ), ) ], @@ -75,12 +73,8 @@ class CourseUnitItem extends StatelessWidget { width: 165.w, decoration: BoxDecoration( image: DecorationImage( - image: AssetImage( - 'gendubeij_mengban'.assetPng - ), - fit: BoxFit.fill - ) - ), + image: AssetImage('gendubeij_mengban'.assetPng), + fit: BoxFit.fill)), alignment: Alignment.center, child: Image.asset( 'iv_lock'.assetPng, diff --git a/lib/pages/user/setting/reback_page.dart b/lib/pages/user/setting/reback_page.dart index 879d586..33edfe0 100644 --- a/lib/pages/user/setting/reback_page.dart +++ b/lib/pages/user/setting/reback_page.dart @@ -26,73 +26,87 @@ class ReBackPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: const WEAppBar( - titleText: '我要反馈', - ), - body: Container( - color: Colors.white, - padding: EdgeInsets.symmetric( - horizontal: 24.w + appBar: const WEAppBar( + titleText: '我要反馈', ), - child: SafeArea( - child: Column( - children: [ - 20.verticalSpace, - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '请输入您要反馈的问题和意见,10-500个字', - textAlign: TextAlign.left, - style: TextStyle( - fontSize: 19.sp, - color: HexColor('#333333') - ), - ), - Text( - '48/500', - textAlign: TextAlign.right, - style: TextStyle( - fontSize: 19.sp, - color: HexColor('#333333') - ),) - ], - ), - 9.5.verticalSpace, - Expanded( - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.fill, - image: AssetImage('bg_reback'.assetPng) + body: Container( + color: Colors.white, + padding: EdgeInsets.symmetric(horizontal: 10.w), + child: SafeArea( + child: LayoutBuilder(builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + children: [ + 20.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '请输入您要反馈的问题和意见,10-500个字', + textAlign: TextAlign.left, + style: TextStyle( + fontSize: 19.sp, color: HexColor('#333333')), + ), + Text( + '48/500', + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 19.sp, color: HexColor('#333333')), + ) + ], + ), + 9.5.verticalSpace, + Expanded( + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage('bg_reback'.assetPng), + fit: BoxFit.fill)), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 10, horizontal: 16), + // 设置对称内边距 + child: TextField( + textInputAction: TextInputAction.done, + decoration: InputDecoration( + border: InputBorder.none, + hintStyle: TextStyle( + fontSize: 16.sp, + color: const Color(0xFF999999))), + ), + ), + ), + ), + 4.5.verticalSpace, + Container( + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.fill, + image: AssetImage(_canEnsure + ? 're_button'.assetPng + : 're_button_dis'.assetPng))), + alignment: Alignment.center, + width: 91.w, + height: 45.h, + child: Text( + '提交', + textAlign: TextAlign.center, + style: + TextStyle(color: Colors.white, fontSize: 17.sp), + ), ) + ], ), ), ), - 4.5.verticalSpace, - Container( - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.fill, - image: AssetImage(_canEnsure?'re_button'.assetPng:'re_button_dis'.assetPng) - ) - ), - alignment: Alignment.center, - width: 91.w, - height: 45.h, - child: Text( - '提交', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 17.sp - ), - ), - ) - ], - ), + ); + }), ), - ) - ); + )); } -} \ No newline at end of file +} diff --git a/lib/pages/user/setting/setting_page.dart b/lib/pages/user/setting/setting_page.dart index b5b62b1..e28a265 100644 --- a/lib/pages/user/setting/setting_page.dart +++ b/lib/pages/user/setting/setting_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:wow_english/common/widgets/we_app_bar.dart'; import '../../../route/route.dart'; @@ -9,14 +10,30 @@ class SettingPage extends StatefulWidget { @override State createState() { - return SettingPageState(); + return SettingPageState(); } } class SettingPageState extends State { + String? _version; + String? _buildNum; + @override + void initState() { + super.initState(); + _retrieveVersionInfo(); + } + + Future _retrieveVersionInfo() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + setState(() { + _version = packageInfo.version; + _buildNum = packageInfo.buildNumber; + }); + } + @override Widget build(BuildContext context) { - return Scaffold( + return Scaffold( appBar: const WEAppBar( titleText: '设置', ), @@ -28,17 +45,18 @@ class SettingPageState extends State { child: ListView( children: [ 34.verticalSpace, - _buildItemWidget('注销账号', onPress: (){ + _buildItemWidget('注销账号', onPress: () { pushNamed(AppRouteName.deleteAccount); }), 12.verticalSpace, - _buildItemWidget('清除缓存', onPress: (){ - - }), + _buildItemWidget('清除缓存', onPress: () {}), 12.verticalSpace, - _buildItemWidget('帮助与反馈', onPress: (){ + _buildItemWidget('帮助与反馈', onPress: () { pushNamed(AppRouteName.reBack); }), + 12.verticalSpace, + _buildItemWidget('Version: $_version Build:$_buildNum', + onPress: () {}), ], ), ), @@ -46,13 +64,15 @@ class SettingPageState extends State { ), ); } - - Widget _buildItemWidget(String text,{VoidCallback? onPress}) { + + Widget _buildItemWidget(String text, {VoidCallback? onPress}) { return OutlinedButton( onPressed: () => onPress?.call(), style: ButtonStyle( - side: MaterialStateProperty.all(BorderSide(color: const Color(0xFF140C10), width: 1.5.w)), - shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), + side: MaterialStateProperty.all( + BorderSide(color: const Color(0xFF140C10), width: 1.5.w)), + shape: MaterialStateProperty.all( + RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), minimumSize: MaterialStateProperty.all(Size(double.infinity, 58.h)), backgroundColor: MaterialStateProperty.all(Colors.white), ), @@ -66,4 +86,4 @@ class SettingPageState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/pages/user/user_page.dart b/lib/pages/user/user_page.dart index 0d8fa27..c57a029 100644 --- a/lib/pages/user/user_page.dart +++ b/lib/pages/user/user_page.dart @@ -173,15 +173,7 @@ class _UserView extends StatelessWidget { UserUtil.getUser()?.phoneNum == '17718485544') ? 12.verticalSpace : 1.verticalSpace), - OutlinedButton( - onPressed: () => pushNamed(AppRouteName.fogPwd), - style: normalButtonStyle, - child: Text( - "修改密码", - style: textStyle21sp, - ), - ), - 12.verticalSpace, + Offstage( offstage: AppConfigHelper.shouldHidePay(), child: OutlinedButton( @@ -199,6 +191,15 @@ class _UserView extends StatelessWidget { style: textStyle21sp, )), ), + 12.verticalSpace, + OutlinedButton( + onPressed: () => pushNamed(AppRouteName.fogPwd), + style: normalButtonStyle, + child: Text( + "修改密码", + style: textStyle21sp, + ), + ), Offstage( offstage: AppConfigHelper.shouldHidePay(), child: 12.verticalSpace, diff --git a/lib/route/route.dart b/lib/route/route.dart index 8fddf58..0bc1908 100644 --- a/lib/route/route.dart +++ b/lib/route/route.dart @@ -17,6 +17,7 @@ import 'package:wow_english/pages/repeatafter/repeat_after_page.dart'; import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart'; import 'package:wow_english/pages/shop/exchane/exchange_lesson_page.dart'; import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.dart'; +import 'package:wow_english/pages/shop/home/shop_desc_page.dart'; import 'package:wow_english/pages/shop/home/shop_home_page.dart'; import 'package:wow_english/pages/user/information/user_information_page.dart'; import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart'; @@ -49,6 +50,7 @@ class AppRouteName { static const String courseSection = 'courseSections'; static const String listen = 'listen'; static const String shop = 'shop'; + static const String shopDesc = 'shopDesc'; static const String exLesson = 'exLesson'; static const String exList = 'exList'; static const String reAfter = 'reAfter'; @@ -141,6 +143,8 @@ class AppRouter { return CupertinoPageRoute(builder: (_) => const ListenPage()); case AppRouteName.shop: return CupertinoPageRoute(builder: (_) => const ShopHomePage()); + case AppRouteName.shopDesc: + return CupertinoPageRoute(builder: (_) => const ShopDescPage()); case AppRouteName.pay: var productEntity = ProductEntity(); if (settings.arguments != null && settings.arguments is ProductEntity) { diff --git a/lib/utils/audio_player_util.dart b/lib/utils/audio_player_util.dart new file mode 100644 index 0000000..b504c70 --- /dev/null +++ b/lib/utils/audio_player_util.dart @@ -0,0 +1,114 @@ +import 'package:audioplayers/audioplayers.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:wow_english/common/extension/string_extension.dart'; + +import 'log_util.dart'; + +enum AudioPlayerUtilType { + welcomeToWow('welcome_to_wow'), + classTime('class_time'), + gameTime('game_time'), + musicTime('music_time'), + readingTime('reading_time'), + videoTime('video_time'), + quizTime('quiz_time'), + countWithMe('count_with_me_instrumental'), + inMyTummy('in_my_tummy_instrumental'), + touch('touch_instrumental'); + + const AudioPlayerUtilType(this.path); + + final String path; +} + +class AudioPlayerUtil extends WidgetsBindingObserver { + static AudioPlayerUtil? _instance; + late AudioPlayer _audioPlayer; + late AudioPlayerUtilType currentType; + bool _wasPlaying = false; + static const TAG = "AudioPlayerUtil"; + + // 私有构造函数 + AudioPlayerUtil._internal() { + // 监听应用生命周期 + WidgetsBinding.instance.addObserver(this); + _audioPlayer = AudioPlayer(); + _audioPlayer.onPlayerStateChanged.listen((event) async { + if (event == PlayerState.completed) { + // 播放结束再次播放 + if (currentType == AudioPlayerUtilType.inMyTummy) { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.inMyTummy); + } + if (currentType == AudioPlayerUtilType.countWithMe) { + AudioPlayerUtil.getInstance() + .playAudio(AudioPlayerUtilType.countWithMe); + } + if (currentType == AudioPlayerUtilType.welcomeToWow) { + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.touch); + } + if (currentType == AudioPlayerUtilType.touch) { + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.touch); + } + } + }); + } + + static AudioPlayerUtil getInstance() { + _instance ??= AudioPlayerUtil._internal(); + return _instance!; + } + +// 播放音频 + Future playAudio(AudioPlayerUtilType type) async { + Log.d("$TAG playAudio $type"); + currentType = type; + String path = type.path; + await _audioPlayer.play(AssetSource(path.assetMp3), volume: 0.5); + await _audioPlayer.onPlayerComplete.first; + } + + // stop + Future stop() async { + Log.d("$TAG stop _audioPlayer.state=${_audioPlayer.state}"); + await _audioPlayer.stop(); + } + + // pause + Future pause() async { + Log.d("$TAG pause _audioPlayer.state=${_audioPlayer.state}"); + if (_audioPlayer.state == PlayerState.playing) { + await _audioPlayer.pause(); + } + } + + // resume + Future resume() async { + Log.d("$TAG resume _audioPlayer.state=${_audioPlayer.state}"); + if (_audioPlayer.state == PlayerState.paused) { + await _audioPlayer.resume(); + } + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) async { + Log.d("$TAG didChangeAppLifecycleState appState=$state _wasPlaying=$_wasPlaying _audioPlayer.state=${_audioPlayer.state}"); + if (state == AppLifecycleState.paused) { + if (_audioPlayer.state == PlayerState.playing) { + _wasPlaying = true; + await pause(); + }; + } else if (state == AppLifecycleState.resumed) { + if (_wasPlaying == true) { + _wasPlaying = false; + await resume(); + } + } + } + + void dispose() { + Log.d("$TAG dispose _audioPlayer.state=${_audioPlayer.state}"); + _audioPlayer.dispose(); + WidgetsBinding.instance.removeObserver(this); + } +}