Commit 7c8ae103377406a71f677a4088c79ba18394ef7f

Authored by 吴启风
2 parents bdd7ee99 fc3c67bf

Merge branch 'work-wqf-20240715'

Showing 44 changed files with 538 additions and 220 deletions
.fvm/flutter_sdk
1 -/Users/stay/fvm/versions/3.19.2  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +/Users/biao/fvm/versions/3.19.2
3 \ No newline at end of file 3 \ No newline at end of file
.fvm/fvm_config.json
1 { 1 {
2 - "flutterSdkVersion": "3.19.2",  
3 - "flavors": {} 2 + "flutterSdkVersion": "3.19.2"
4 } 3 }
5 \ No newline at end of file 4 \ No newline at end of file
.vscode/settings.json 0 → 100644
  1 +{
  2 + "dart.flutterSdkPath": ".fvm/versions/3.19.2"
  3 +}
0 \ No newline at end of file 4 \ No newline at end of file
assets/images/micro_phone.gif

17.3 KB | W: | H:

17.2 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
assets/images/read_background.png 0 → 100644

480 KB

assets/images/reade_answer.gif

5.31 KB | W: | H:

4.78 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
assets/images/shop_desc.png 0 → 100644

746 KB

assets/images/voice.png

8.3 KB | W: | H:

8.91 KB | W: | H:

  • 2-up
  • Swipe
  • Onion skin
assets/images/xe_shop.png 0 → 100644

267 KB

assets/sounds/class_time.mp3 0 → 100644
No preview for this file type
assets/sounds/count_with_me_instrumental.mp3 0 → 100644
No preview for this file type
assets/sounds/game_time.mp3 0 → 100644
No preview for this file type
assets/sounds/in_my_tummy_instrumental.mp3 0 → 100644
No preview for this file type
assets/sounds/music_time.mp3 0 → 100644
No preview for this file type
assets/sounds/quiz_time.mp3 0 → 100644
No preview for this file type
assets/sounds/reading_time.mp3 0 → 100644
No preview for this file type
assets/sounds/touch_instrumental.mp3 0 → 100644
No preview for this file type
assets/sounds/video_time.mp3 0 → 100644
No preview for this file type
assets/sounds/welcome_to_wow.mp3 0 → 100644
No preview for this file type
ios/Runner.xcodeproj/project.pbxproj
@@ -2327,7 +2327,7 @@ @@ -2327,7 +2327,7 @@
2327 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 2327 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
2328 CODE_SIGN_IDENTITY = "Apple Development"; 2328 CODE_SIGN_IDENTITY = "Apple Development";
2329 CODE_SIGN_STYLE = Automatic; 2329 CODE_SIGN_STYLE = Automatic;
2330 - CURRENT_PROJECT_VERSION = 13; 2330 + CURRENT_PROJECT_VERSION = 16;
2331 DEVELOPMENT_TEAM = T8P9KW8GWH; 2331 DEVELOPMENT_TEAM = T8P9KW8GWH;
2332 ENABLE_BITCODE = NO; 2332 ENABLE_BITCODE = NO;
2333 INFOPLIST_FILE = Runner/Info.plist; 2333 INFOPLIST_FILE = Runner/Info.plist;
@@ -2671,7 +2671,7 @@ @@ -2671,7 +2671,7 @@
2671 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 2671 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
2672 CODE_SIGN_IDENTITY = "Apple Development"; 2672 CODE_SIGN_IDENTITY = "Apple Development";
2673 CODE_SIGN_STYLE = Automatic; 2673 CODE_SIGN_STYLE = Automatic;
2674 - CURRENT_PROJECT_VERSION = 13; 2674 + CURRENT_PROJECT_VERSION = 16;
2675 DEVELOPMENT_TEAM = T8P9KW8GWH; 2675 DEVELOPMENT_TEAM = T8P9KW8GWH;
2676 ENABLE_BITCODE = NO; 2676 ENABLE_BITCODE = NO;
2677 HEADER_SEARCH_PATHS = ( 2677 HEADER_SEARCH_PATHS = (
@@ -2876,7 +2876,7 @@ @@ -2876,7 +2876,7 @@
2876 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; 2876 CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
2877 CODE_SIGN_IDENTITY = "Apple Development"; 2877 CODE_SIGN_IDENTITY = "Apple Development";
2878 CODE_SIGN_STYLE = Automatic; 2878 CODE_SIGN_STYLE = Automatic;
2879 - CURRENT_PROJECT_VERSION = 13; 2879 + CURRENT_PROJECT_VERSION = 16;
2880 DEVELOPMENT_TEAM = T8P9KW8GWH; 2880 DEVELOPMENT_TEAM = T8P9KW8GWH;
2881 ENABLE_BITCODE = NO; 2881 ENABLE_BITCODE = NO;
2882 INFOPLIST_FILE = Runner/Info.plist; 2882 INFOPLIST_FILE = Runner/Info.plist;
ios/Runner/Info.plist
@@ -77,11 +77,11 @@ @@ -77,11 +77,11 @@
77 <true/> 77 <true/>
78 </dict> 78 </dict>
79 <key>NSCameraUsageDescription</key> 79 <key>NSCameraUsageDescription</key>
80 - <string>需要访问相机完成拍照</string> 80 + <string>需要访问相机,完成修改头像功能</string>
81 <key>NSMicrophoneUsageDescription</key> 81 <key>NSMicrophoneUsageDescription</key>
82 - <string>需要获取录音完成后续功能</string> 82 + <string>需要获取录音,完成上课功能</string>
83 <key>NSPhotoLibraryUsageDescription</key> 83 <key>NSPhotoLibraryUsageDescription</key>
84 - <string>需要获取照片用来修改头像</string> 84 + <string>需要获取照片,完成上传头像功能</string>
85 <key>UIApplicationSupportsIndirectInputEvents</key> 85 <key>UIApplicationSupportsIndirectInputEvents</key>
86 <true/> 86 <true/>
87 <key>UILaunchStoryboardName</key> 87 <key>UILaunchStoryboardName</key>
lib/common/core/app_consts.dart
@@ -2,16 +2,26 @@ import &#39;../request/basic_config.dart&#39;; @@ -2,16 +2,26 @@ import &#39;../request/basic_config.dart&#39;;
2 2
3 class AppConsts { 3 class AppConsts {
4 /// 隐私协议 4 /// 隐私协议
5 - static const String userPrivacyPolicyUrl = 'http://page.kouyuxingqiu.com/wowenglishuserregister.html'; 5 + static const String userPrivacyPolicyUrl =
  6 + 'http://page.kouyuxingqiu.com/wowenglishuserregister.html';
6 7
7 /// 儿童隐私协议 8 /// 儿童隐私协议
8 - static const String childrenPrivacyPolicyUrl = 'http://page.kouyuxingqiu.com/wowenglishchildprotect.html'; 9 + static const String childrenPrivacyPolicyUrl =
  10 + 'http://page.kouyuxingqiu.com/wowenglishchildprotect.html';
  11 +
  12 + /// 小鵝通
  13 + static const String xiaoeShopUrl = 'https://appo61s7g678876.h5.xiaoeknow.com';
9 14
10 /// 与第三方共享协议 15 /// 与第三方共享协议
11 - static const String userTermSdkUrl = 'http://page.kouyuxingqiu.com/term_sdk.html'; 16 + static const String userTermSdkUrl =
  17 + 'http://page.kouyuxingqiu.com/term_sdk.html';
12 18
13 /// 先声SDK 19 /// 先声SDK
14 - static String xsAppKey = 'a418';  
15 - static String xsAppSecretKey = BasicConfig.isTestDev?'1a16f31f2611bf32fb7b3fc38f5b2c81':'c11163aa6c834a028da4a4b30955be99';  
16 - static String xsAppService = BasicConfig.isTestDev?'ws://trial.cloud.ssapi.cn:8080':'"wss://api.cloud.ssapi.cn'; 20 + static String xsAppKey = 'a418';
  21 + static String xsAppSecretKey = BasicConfig.isTestDev
  22 + ? '1a16f31f2611bf32fb7b3fc38f5b2c81'
  23 + : 'c11163aa6c834a028da4a4b30955be99';
  24 + static String xsAppService = BasicConfig.isTestDev
  25 + ? 'ws://trial.cloud.ssapi.cn:8080'
  26 + : '"wss://api.cloud.ssapi.cn';
17 } 27 }
lib/pages/games/bloc.dart
@@ -2,17 +2,17 @@ import &#39;package:bloc/bloc.dart&#39;; @@ -2,17 +2,17 @@ import &#39;package:bloc/bloc.dart&#39;;
2 import 'package:flutter/cupertino.dart'; 2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/services.dart'; 3 import 'package:flutter/services.dart';
4 import 'package:wow_english/common/extension/string_extension.dart'; 4 import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/utils/audio_player_util.dart';
5 6
6 import 'event.dart'; 7 import 'event.dart';
7 import 'game_entity.dart'; 8 import 'game_entity.dart';
8 import 'state.dart'; 9 import 'state.dart';
9 10
10 class GamesBloc extends Bloc<GamesEvent, GamesState> { 11 class GamesBloc extends Bloc<GamesEvent, GamesState> {
11 -  
12 late MethodChannel _methodChannel; 12 late MethodChannel _methodChannel;
13 13
14 //手动初始化4个GameEntity对象 14 //手动初始化4个GameEntity对象
15 - final List<GameEntity> _games = [ 15 + final List<GameEntity> _games = [
16 GameEntity() 16 GameEntity()
17 ..id = 1 17 ..id = 1
18 ..imageName = 'game_food_1'.assetPng 18 ..imageName = 'game_food_1'.assetPng
@@ -31,7 +31,7 @@ class GamesBloc extends Bloc&lt;GamesEvent, GamesState&gt; { @@ -31,7 +31,7 @@ class GamesBloc extends Bloc&lt;GamesEvent, GamesState&gt; {
31 ..name = 'Animal' 31 ..name = 'Animal'
32 ]; 32 ];
33 33
34 - List<GameEntity> get listData => _games; 34 + List<GameEntity> get listData => _games;
35 35
36 GamesBloc() : super(GamesState().init()) { 36 GamesBloc() : super(GamesState().init()) {
37 on<InitEvent>(_init); 37 on<InitEvent>(_init);
@@ -39,13 +39,16 @@ class GamesBloc extends Bloc&lt;GamesEvent, GamesState&gt; { @@ -39,13 +39,16 @@ class GamesBloc extends Bloc&lt;GamesEvent, GamesState&gt; {
39 } 39 }
40 40
41 void _init(InitEvent event, Emitter<GamesState> emit) async { 41 void _init(InitEvent event, Emitter<GamesState> emit) async {
  42 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.inMyTummy);
42 emit(state.clone()); 43 emit(state.clone());
43 } 44 }
44 45
45 void _gotoGamePage(GotoGamePageEvent event, Emitter<GamesState> emit) async { 46 void _gotoGamePage(GotoGamePageEvent event, Emitter<GamesState> emit) async {
  47 + await AudioPlayerUtil.getInstance().pause();
46 try { 48 try {
47 _methodChannel = const MethodChannel('wow_english/game_method_channel'); 49 _methodChannel = const MethodChannel('wow_english/game_method_channel');
48 - await _methodChannel.invokeMethod('openGamePage', { "gameId": event.gameId }); 50 + await _methodChannel
  51 + .invokeMethod('openGamePage', {"gameId": event.gameId});
49 } on PlatformException catch (e) { 52 } on PlatformException catch (e) {
50 debugPrint("Failed to go to native page: '${e.message}'."); 53 debugPrint("Failed to go to native page: '${e.message}'.");
51 } 54 }
lib/pages/home/bloc.dart
  1 +import 'package:audioplayers/audioplayers.dart';
1 import 'package:bloc/bloc.dart'; 2 import 'package:bloc/bloc.dart';
  3 +import 'package:wow_english/common/core/user_util.dart';
  4 +import 'package:wow_english/common/extension/string_extension.dart';
  5 +import 'package:wow_english/utils/audio_player_util.dart';
2 6
3 import '../../common/core/app_config_helper.dart'; 7 import '../../common/core/app_config_helper.dart';
4 import '../../common/request/dao/system_dao.dart'; 8 import '../../common/request/dao/system_dao.dart';
@@ -16,6 +20,9 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; { @@ -16,6 +20,9 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
16 bool exchangeResult = false; 20 bool exchangeResult = false;
17 21
18 void _init(InitEvent event, Emitter<HomeState> emit) async { 22 void _init(InitEvent event, Emitter<HomeState> emit) async {
  23 + if (UserUtil.isLogined()) {
  24 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.welcomeToWow);
  25 + }
19 await _checkUpdate(emit); 26 await _checkUpdate(emit);
20 } 27 }
21 28
@@ -37,7 +44,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; { @@ -37,7 +44,7 @@ class HomeBloc extends Bloc&lt;HomeEvent, HomeState&gt; {
37 return; 44 return;
38 } 45 }
39 Log.d( 46 Log.d(
40 - "WQF _checkUpdate appVersionEntity: $appVersionEntity localVersion=$localVersion"); 47 + "HomeBloc _checkUpdate appVersionEntity: $appVersionEntity localVersion=$localVersion");
41 if (localVersion < int.parse(appVersionEntity.version ?? '0')) { 48 if (localVersion < int.parse(appVersionEntity.version ?? '0')) {
42 emit(UpdateDialogState( 49 emit(UpdateDialogState(
43 appVersionEntity.volType == UpdateStrategy.FORCE.name, 50 appVersionEntity.volType == UpdateStrategy.FORCE.name,
lib/pages/home/view.dart
  1 +import 'package:audioplayers/audioplayers.dart';
1 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
2 import 'package:flutter_app_update/azhon_app_update.dart'; 3 import 'package:flutter_app_update/azhon_app_update.dart';
3 import 'package:flutter_app_update/update_model.dart'; 4 import 'package:flutter_app_update/update_model.dart';
4 import 'package:flutter_bloc/flutter_bloc.dart'; 5 import 'package:flutter_bloc/flutter_bloc.dart';
5 import 'package:url_launcher/url_launcher.dart'; 6 import 'package:url_launcher/url_launcher.dart';
6 import 'package:wow_english/common/core/app_config_helper.dart'; 7 import 'package:wow_english/common/core/app_config_helper.dart';
  8 +import 'package:wow_english/common/core/app_consts.dart';
7 import 'package:wow_english/common/extension/string_extension.dart'; 9 import 'package:wow_english/common/extension/string_extension.dart';
8 import 'package:wow_english/models/app_version_entity.dart'; 10 import 'package:wow_english/models/app_version_entity.dart';
9 import 'package:wow_english/pages/home/state.dart'; 11 import 'package:wow_english/pages/home/state.dart';
10 import 'package:wow_english/pages/home/widgets/BaseHomeHeaderWidget.dart'; 12 import 'package:wow_english/pages/home/widgets/BaseHomeHeaderWidget.dart';
11 import 'package:wow_english/pages/shop/exchane/bloc/exchange_lesson_bloc.dart'; 13 import 'package:wow_english/pages/shop/exchane/bloc/exchange_lesson_bloc.dart';
12 import 'package:wow_english/pages/user/bloc/user_bloc.dart'; 14 import 'package:wow_english/pages/user/bloc/user_bloc.dart';
  15 +import 'package:wow_english/utils/audio_player_util.dart';
13 16
14 import '../../common/core/user_util.dart'; 17 import '../../common/core/user_util.dart';
15 import '../../common/dialogs/show_dialog.dart'; 18 import '../../common/dialogs/show_dialog.dart';
@@ -57,7 +60,9 @@ class _HomePageView extends StatelessWidget { @@ -57,7 +60,9 @@ class _HomePageView extends StatelessWidget {
57 child: Column( 60 child: Column(
58 children: [ 61 children: [
59 BaseHomeHeaderWidget( 62 BaseHomeHeaderWidget(
60 - callBack: (value) => { 63 + callBack: (value) async => {
  64 + await AudioPlayerUtil.getInstance()
  65 + .playAudio(AudioPlayerUtilType.touch),
61 bloc.exchangeResult = value['exchange'], 66 bloc.exchangeResult = value['exchange'],
62 bloc.add(ExchangeSuccessEvent()) 67 bloc.add(ExchangeSuccessEvent())
63 }), 68 }),
@@ -68,7 +73,9 @@ class _HomePageView extends StatelessWidget { @@ -68,7 +73,9 @@ class _HomePageView extends StatelessWidget {
68 Expanded( 73 Expanded(
69 child: GestureDetector( 74 child: GestureDetector(
70 onTap: () { 75 onTap: () {
71 - _checkPermission(() { 76 + _checkPermission(() async {
  77 + await AudioPlayerUtil.getInstance()
  78 + .playAudio(AudioPlayerUtilType.classTime);
72 pushNamed(AppRouteName.courseUnit) 79 pushNamed(AppRouteName.courseUnit)
73 .then((value) => { 80 .then((value) => {
74 if (value != null) 81 if (value != null)
@@ -102,13 +109,45 @@ class _HomePageView extends StatelessWidget { @@ -102,13 +109,45 @@ class _HomePageView extends StatelessWidget {
102 ), 109 ),
103 ), 110 ),
104 ), 111 ),
  112 + BlocBuilder<UserBloc, UserState>(
  113 + builder: (context, userState) {
  114 + return GestureDetector(
  115 + onTap: () {
  116 + _checkPermission(() async {
  117 + await AudioPlayerUtil.getInstance().pause();
  118 + Navigator.of(context).pushNamed(
  119 + AppRouteName.webView,
  120 + arguments: {
  121 + 'urlStr': AppConsts.xiaoeShopUrl,
  122 + 'webViewTitle': 'Wow精选'
  123 + }).then((value) async => {
  124 + await AudioPlayerUtil.getInstance().playAudio(
  125 + AudioPlayerUtilType.touch),
  126 + });
  127 + }, bloc);
  128 + },
  129 + child: Offstage(
  130 + offstage: AppConfigHelper.shouldHidePay() ||
  131 + !UserUtil.isLogined(),
  132 + child: Image.asset('xe_shop'.assetPng,
  133 + width: 140.5.w, height: 172.h),
  134 + ));
  135 + }),
105 Expanded( 136 Expanded(
106 child: BlocBuilder<UserBloc, UserState>( 137 child: BlocBuilder<UserBloc, UserState>(
107 builder: (context, userState) { 138 builder: (context, userState) {
108 return GestureDetector( 139 return GestureDetector(
109 onTap: () { 140 onTap: () {
110 - _checkPermission(() {  
111 - pushNamed(AppRouteName.games); 141 + _checkPermission(() async {
  142 + await AudioPlayerUtil.getInstance()
  143 + .playAudio(
  144 + AudioPlayerUtilType.gameTime);
  145 + pushNamed(AppRouteName.games)
  146 + .then((value) => {
  147 + AudioPlayerUtil.getInstance()
  148 + .playAudio(AudioPlayerUtilType
  149 + .touch),
  150 + });
112 }, bloc); 151 }, bloc);
113 }, 152 },
114 child: Column( 153 child: Column(
@@ -157,6 +196,8 @@ class _HomePageView extends StatelessWidget { @@ -157,6 +196,8 @@ class _HomePageView extends StatelessWidget {
157 }, rightTap: () { 196 }, rightTap: () {
158 popPage(); 197 popPage();
159 pushNamed(AppRouteName.shop).then((value) { 198 pushNamed(AppRouteName.shop).then((value) {
  199 + AudioPlayerUtil.getInstance()
  200 + .playAudio(AudioPlayerUtilType.touch);
160 if (value != null) { 201 if (value != null) {
161 bloc.exchangeResult = value['exchange']; 202 bloc.exchangeResult = value['exchange'];
162 bloc.add(ExchangeSuccessEvent()); 203 bloc.add(ExchangeSuccessEvent());
lib/pages/home/widgets/BaseHomeHeaderWidget.dart
@@ -3,6 +3,7 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;; @@ -3,6 +3,7 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
3 import 'package:flutter_screenutil/flutter_screenutil.dart'; 3 import 'package:flutter_screenutil/flutter_screenutil.dart';
4 import 'package:wow_english/common/core/app_config_helper.dart'; 4 import 'package:wow_english/common/core/app_config_helper.dart';
5 import 'package:wow_english/common/extension/string_extension.dart'; 5 import 'package:wow_english/common/extension/string_extension.dart';
  6 +import 'package:wow_english/utils/audio_player_util.dart';
6 7
7 import '../../../common/core/user_util.dart'; 8 import '../../../common/core/user_util.dart';
8 import '../../../models/course_entity.dart'; 9 import '../../../models/course_entity.dart';
@@ -83,7 +84,8 @@ class BaseHomeHeaderWidget extends StatelessWidget { @@ -83,7 +84,8 @@ class BaseHomeHeaderWidget extends StatelessWidget {
83 offstage: AppConfigHelper.shouldHidePay() || 84 offstage: AppConfigHelper.shouldHidePay() ||
84 !UserUtil.isLogined(), 85 !UserUtil.isLogined(),
85 child: GestureDetector( 86 child: GestureDetector(
86 - onTap: () => { 87 + onTap: () async => {
  88 + await AudioPlayerUtil.getInstance().pause(),
87 pushNamed(AppRouteName.shop).then((value) { 89 pushNamed(AppRouteName.shop).then((value) {
88 if (value != null) { 90 if (value != null) {
89 if (callBack == null) { 91 if (callBack == null) {
@@ -113,8 +115,9 @@ class BaseHomeHeaderWidget extends StatelessWidget { @@ -113,8 +115,9 @@ class BaseHomeHeaderWidget extends StatelessWidget {
113 ); 115 );
114 } 116 }
115 117
116 - void onUserClick() { 118 + Future<void> onUserClick() async {
117 if (UserUtil.isLogined()) { 119 if (UserUtil.isLogined()) {
  120 + await AudioPlayerUtil.getInstance().pause();
118 pushNamed(AppRouteName.user).then((value) { 121 pushNamed(AppRouteName.user).then((value) {
119 if (value != null) { 122 if (value != null) {
120 if (callBack == null) { 123 if (callBack == null) {
lib/pages/practice/topic_picture_page.dart
@@ -59,7 +59,12 @@ class _TopicPicturePage extends StatelessWidget { @@ -59,7 +59,12 @@ class _TopicPicturePage extends StatelessWidget {
59 builder: (context, state) { 59 builder: (context, state) {
60 final bloc = BlocProvider.of<TopicPictureBloc>(context); 60 final bloc = BlocProvider.of<TopicPictureBloc>(context);
61 return Container( 61 return Container(
62 - color: Colors.white, 62 + decoration: BoxDecoration(
  63 + image: DecorationImage(
  64 + image: AssetImage('read_background'.assetPng), // 背景图片路径
  65 + fit: BoxFit.cover, // 适应图片的方式
  66 + ),
  67 + ),
63 child: Stack( 68 child: Stack(
64 children: [ 69 children: [
65 Column( 70 Column(
@@ -75,6 +80,7 @@ class _TopicPicturePage extends StatelessWidget { @@ -75,6 +80,7 @@ class _TopicPicturePage extends StatelessWidget {
75 // Navigator.pop(context); 80 // Navigator.pop(context);
76 }, 81 },
77 ), 82 ),
  83 + 35.verticalSpace,
78 Expanded( 84 Expanded(
79 child: PageView.builder( 85 child: PageView.builder(
80 itemCount: bloc.entity?.topics?.length, 86 itemCount: bloc.entity?.topics?.length,
@@ -109,12 +115,7 @@ class _TopicPicturePage extends StatelessWidget { @@ -109,12 +115,7 @@ class _TopicPicturePage extends StatelessWidget {
109 }), 115 }),
110 ) 116 )
111 ], 117 ],
112 - ),  
113 - Positioned(  
114 - left: 0,  
115 - right: 0,  
116 - bottom: 0,  
117 - child: Image.asset('bottom_grass'.assetPng)) 118 + )
118 ], 119 ],
119 ), 120 ),
120 ); 121 );
@@ -299,7 +300,7 @@ class _TopicPicturePage extends StatelessWidget { @@ -299,7 +300,7 @@ class _TopicPicturePage extends StatelessWidget {
299 26.verticalSpace, 300 26.verticalSpace,
300 SizedBox( 301 SizedBox(
301 height: 143.h, 302 height: 143.h,
302 - width: 163.w * (topics?.topicAnswerList?.length ?? 0), 303 + width: 203.w * (topics?.topicAnswerList?.length ?? 0),
303 child: ListView.builder( 304 child: ListView.builder(
304 scrollDirection: Axis.horizontal, 305 scrollDirection: Axis.horizontal,
305 physics: const NeverScrollableScrollPhysics(), 306 physics: const NeverScrollableScrollPhysics(),
@@ -321,7 +322,7 @@ class _TopicPicturePage extends StatelessWidget { @@ -321,7 +322,7 @@ class _TopicPicturePage extends StatelessWidget {
321 builder: (context, state) { 322 builder: (context, state) {
322 final bloc = BlocProvider.of<TopicPictureBloc>(context); 323 final bloc = BlocProvider.of<TopicPictureBloc>(context);
323 return Container( 324 return Container(
324 - padding: EdgeInsets.symmetric(horizontal: 10.w), 325 + padding: EdgeInsets.symmetric(horizontal: 20.w),
325 child: GestureDetector( 326 child: GestureDetector(
326 onTap: () => bloc.add(SelectItemEvent(index)), 327 onTap: () => bloc.add(SelectItemEvent(index)),
327 child: Container( 328 child: Container(
@@ -333,15 +334,13 @@ class _TopicPicturePage extends StatelessWidget { @@ -333,15 +334,13 @@ class _TopicPicturePage extends StatelessWidget {
333 borderRadius: BorderRadius.circular(15), 334 borderRadius: BorderRadius.circular(15),
334 ), 335 ),
335 height: 143.h, 336 height: 143.h,
336 - width: 143.w, 337 + width: 163.w,
337 child: Container( 338 child: Container(
338 decoration: BoxDecoration( 339 decoration: BoxDecoration(
339 color: Colors.white, 340 color: Colors.white,
340 borderRadius: BorderRadius.circular(15), 341 borderRadius: BorderRadius.circular(15),
341 - border: Border.all(  
342 - width: 1.0, color: const Color(0xFF140C10)),  
343 image: DecorationImage( 342 image: DecorationImage(
344 - fit: BoxFit.fitWidth, 343 + fit: BoxFit.fill,
345 image: NetworkImage(answerList?.picUrl ?? ''))), 344 image: NetworkImage(answerList?.picUrl ?? ''))),
346 ), 345 ),
347 ), 346 ),
@@ -449,12 +448,18 @@ class _TopicPicturePage extends StatelessWidget { @@ -449,12 +448,18 @@ class _TopicPicturePage extends StatelessWidget {
449 return Row( 448 return Row(
450 mainAxisAlignment: MainAxisAlignment.center, 449 mainAxisAlignment: MainAxisAlignment.center,
451 children: [ 450 children: [
452 - OwImageWidget(  
453 - name: topics?.picUrl ?? '',  
454 - height: 186.h,  
455 - width: 186.w, 451 + ClipRRect(
  452 + borderRadius: BorderRadius.circular(20),
  453 + child: Container(
  454 + color: Colors.white,
  455 + child: OwImageWidget(
  456 + name: topics?.picUrl ?? '',
  457 + height: 186.h,
  458 + width: 186.w,
  459 + ),
  460 + ),
456 ), 461 ),
457 - 160.horizontalSpace, 462 + 120.horizontalSpace,
458 Column( 463 Column(
459 mainAxisAlignment: MainAxisAlignment.center, 464 mainAxisAlignment: MainAxisAlignment.center,
460 children: [ 465 children: [
@@ -472,8 +477,8 @@ class _TopicPicturePage extends StatelessWidget { @@ -472,8 +477,8 @@ class _TopicPicturePage extends StatelessWidget {
472 bloc.voicePlayState == VoicePlayState.playing 477 bloc.voicePlayState == VoicePlayState.playing
473 ? 'reade_answer'.assetGif 478 ? 'reade_answer'.assetGif
474 : 'voice'.assetPng, 479 : 'voice'.assetPng,
475 - height: 52.h,  
476 - width: 46.w, 480 + height: 45.h,
  481 + width: 45.w,
477 ), 482 ),
478 10.horizontalSpace, 483 10.horizontalSpace,
479 Text(topics?.word ?? '') 484 Text(topics?.word ?? '')
@@ -506,8 +511,8 @@ class _TopicPicturePage extends StatelessWidget { @@ -506,8 +511,8 @@ class _TopicPicturePage extends StatelessWidget {
506 bloc.isVoicing 511 bloc.isVoicing
507 ? 'micro_phone'.assetGif 512 ? 'micro_phone'.assetGif
508 : 'micro_phone'.assetPng, 513 : 'micro_phone'.assetPng,
509 - height: 75.w,  
510 - width: 75.w, 514 + height: 46.h,
  515 + width: 46.w,
511 ), 516 ),
512 ) 517 )
513 ], 518 ],
lib/pages/practice/widgets/practice_header_widget.dart
@@ -12,9 +12,10 @@ class PracticeHeaderWidget extends StatelessWidget { @@ -12,9 +12,10 @@ class PracticeHeaderWidget extends StatelessWidget {
12 @override 12 @override
13 Widget build(BuildContext context) { 13 Widget build(BuildContext context) {
14 return Container( 14 return Container(
15 - color: Colors.white, 15 + color: Colors.transparent,
16 height: kToolbarHeight + 3.h, 16 height: kToolbarHeight + 3.h,
17 child: AppBar( 17 child: AppBar(
  18 + backgroundColor: Colors.transparent,
18 leading: GestureDetector( 19 leading: GestureDetector(
19 child: Image.asset( 20 child: Image.asset(
20 'back_around'.assetPng, 21 'back_around'.assetPng,
@@ -25,7 +26,7 @@ class PracticeHeaderWidget extends StatelessWidget { @@ -25,7 +26,7 @@ class PracticeHeaderWidget extends StatelessWidget {
25 ), 26 ),
26 centerTitle: true, 27 centerTitle: true,
27 title: Container( 28 title: Container(
28 - height: 40.h, 29 + height: 20.h,
29 width: 100.w, // 容器宽度 30 width: 100.w, // 容器宽度
30 // padding: EdgeInsets.symmetric(horizontal: 27.w, vertical: 10.h), 31 // padding: EdgeInsets.symmetric(horizontal: 27.w, vertical: 10.h),
31 alignment: Alignment.center, 32 alignment: Alignment.center,
lib/pages/reading/bloc/reading_event.dart
@@ -39,7 +39,7 @@ class XSVoiceStartEvent extends ReadingPageEvent { @@ -39,7 +39,7 @@ class XSVoiceStartEvent extends ReadingPageEvent {
39 final String content; 39 final String content;
40 final String type; 40 final String type;
41 final String? userId; 41 final String? userId;
42 - XSVoiceStartEvent(this.content,this.type,this.userId); 42 + XSVoiceStartEvent(this.content, this.type, this.userId);
43 } 43 }
44 44
45 ///先声评测停止 45 ///先声评测停止
@@ -52,4 +52,7 @@ class OnXSVoiceStateChangeEvent extends ReadingPageEvent {} @@ -52,4 +52,7 @@ class OnXSVoiceStateChangeEvent extends ReadingPageEvent {}
52 class VoicePlayStateChangeEvent extends ReadingPageEvent {} 52 class VoicePlayStateChangeEvent extends ReadingPageEvent {}
53 53
54 ///录音播放 54 ///录音播放
55 -class PlayRecordAudioEvent extends ReadingPageEvent {}  
56 \ No newline at end of file 55 \ No newline at end of file
  56 +class PlayRecordAudioEvent extends ReadingPageEvent {}
  57 +
  58 +///播放下一页
  59 +class PlayNextPageEvent extends ReadingPageEvent {}
lib/pages/reading/reading_page.dart
@@ -21,17 +21,16 @@ class ReadingPage extends StatelessWidget { @@ -21,17 +21,16 @@ class ReadingPage extends StatelessWidget {
21 @override 21 @override
22 Widget build(BuildContext context) { 22 Widget build(BuildContext context) {
23 return BlocProvider( 23 return BlocProvider(
24 - create: (_) => ReadingPageBloc(context, PageController(), courseLessonId ?? '')  
25 - ..add(InitBlocEvent())  
26 - ..add(RequestDataEvent())  
27 - ..add(XSVoiceInitEvent(  
28 - {  
29 - 'appKey':AppConsts.xsAppKey,  
30 - 'service':AppConsts.xsAppService,  
31 - 'secretKey':AppConsts.xsAppSecretKey,  
32 - 'userId':UserUtil.getUser()!.id.toString(),  
33 - }  
34 - )), 24 + create: (_) =>
  25 + ReadingPageBloc(context, PageController(), courseLessonId ?? '')
  26 + ..add(InitBlocEvent())
  27 + ..add(RequestDataEvent())
  28 + ..add(XSVoiceInitEvent({
  29 + 'appKey': AppConsts.xsAppKey,
  30 + 'service': AppConsts.xsAppService,
  31 + 'secretKey': AppConsts.xsAppSecretKey,
  32 + 'userId': UserUtil.getUser()!.id.toString(),
  33 + })),
35 child: _ReadingPage(), 34 child: _ReadingPage(),
36 ); 35 );
37 } 36 }
@@ -60,8 +59,8 @@ class _ReadingPage extends StatelessWidget { @@ -60,8 +59,8 @@ class _ReadingPage extends StatelessWidget {
60 ); 59 );
61 } 60 }
62 61
63 - Widget _readingPageView() => BlocBuilder<ReadingPageBloc, ReadingPageState>(  
64 - builder: (context, state) { 62 + Widget _readingPageView() =>
  63 + BlocBuilder<ReadingPageBloc, ReadingPageState>(builder: (context, state) {
65 final bloc = BlocProvider.of<ReadingPageBloc>(context); 64 final bloc = BlocProvider.of<ReadingPageBloc>(context);
66 return Container( 65 return Container(
67 color: Colors.white, 66 color: Colors.white,
@@ -84,16 +83,14 @@ class _ReadingPage extends StatelessWidget { @@ -84,16 +83,14 @@ class _ReadingPage extends StatelessWidget {
84 children: [ 83 children: [
85 Padding( 84 Padding(
86 padding: 85 padding:
87 - EdgeInsets.only(left: ScreenUtil().bottomBarHeight), 86 + EdgeInsets.only(left: ScreenUtil().bottomBarHeight),
88 child: IconButton( 87 child: IconButton(
89 onPressed: () { 88 onPressed: () {
90 - popPage(  
91 - data:{  
92 - 'currentStep':bloc.currentPage,  
93 - 'courseLessonId':bloc.courseLessonId,  
94 - 'isCompleted':bloc.isLastPage(),  
95 - }  
96 - ); 89 + popPage(data: {
  90 + 'currentStep': bloc.currentPage,
  91 + 'courseLessonId': bloc.courseLessonId,
  92 + 'isCompleted': bloc.isLastPage(),
  93 + });
97 }, 94 },
98 icon: Image.asset( 95 icon: Image.asset(
99 'back_around'.assetPng, 96 'back_around'.assetPng,
@@ -158,6 +155,7 @@ class _ReadingPage extends StatelessWidget { @@ -158,6 +155,7 @@ class _ReadingPage extends StatelessWidget {
158 margin: EdgeInsets.symmetric(horizontal: 10.w), 155 margin: EdgeInsets.symmetric(horizontal: 10.w),
159 child: Row( 156 child: Row(
160 children: [ 157 children: [
  158 + 5.horizontalSpace,
161 GestureDetector( 159 GestureDetector(
162 onTap: () { 160 onTap: () {
163 if (bloc.isRecording) { 161 if (bloc.isRecording) {
@@ -167,7 +165,7 @@ class _ReadingPage extends StatelessWidget { @@ -167,7 +165,7 @@ class _ReadingPage extends StatelessWidget {
167 }, 165 },
168 child: Image.asset( 166 child: Image.asset(
169 bloc.voicePlayState == VoicePlayState.playing && 167 bloc.voicePlayState == VoicePlayState.playing &&
170 - bloc.isOriginAudioPlaying 168 + bloc.isOriginAudioPlaying
171 ? 'reade_answer'.assetGif 169 ? 'reade_answer'.assetGif
172 : 'voice'.assetPng, 170 : 'voice'.assetPng,
173 height: 40.h, 171 height: 40.h,
@@ -179,12 +177,12 @@ class _ReadingPage extends StatelessWidget { @@ -179,12 +177,12 @@ class _ReadingPage extends StatelessWidget {
179 ), 177 ),
180 Expanded( 178 Expanded(
181 child: Text( 179 child: Text(
182 - bloc.currentPageData()?.word?.trim() ?? '',  
183 - style: TextStyle(  
184 - color: const Color(0xFF333333), fontSize: 21.sp),  
185 - maxLines: 2,  
186 - overflow: TextOverflow.ellipsis,  
187 - )), 180 + bloc.currentPageData()?.word?.trim() ?? '',
  181 + style: TextStyle(
  182 + color: const Color(0xFF333333), fontSize: 21.sp),
  183 + maxLines: 2,
  184 + overflow: TextOverflow.ellipsis,
  185 + )),
188 SizedBox( 186 SizedBox(
189 width: 10.w, 187 width: 10.w,
190 ), 188 ),
@@ -241,8 +239,7 @@ class _ReadingPage extends StatelessWidget { @@ -241,8 +239,7 @@ class _ReadingPage extends StatelessWidget {
241 return Stack( 239 return Stack(
242 children: [ 240 children: [
243 Positioned.fill( 241 Positioned.fill(
244 - child:  
245 - Image.network(readings.picUrl ?? '', fit: BoxFit.cover), 242 + child: Image.network(readings.picUrl ?? '', fit: BoxFit.cover),
246 ), 243 ),
247 ], 244 ],
248 ); 245 );
lib/pages/section/bloc/section_bloc.dart
  1 +import 'package:audioplayers/audioplayers.dart';
1 import 'package:flutter/cupertino.dart'; 2 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/foundation.dart'; 3 import 'package:flutter/foundation.dart';
3 import 'package:flutter/material.dart'; 4 import 'package:flutter/material.dart';
4 import 'package:flutter_bloc/flutter_bloc.dart'; 5 import 'package:flutter_bloc/flutter_bloc.dart';
5 import 'package:flutter_screenutil/flutter_screenutil.dart'; 6 import 'package:flutter_screenutil/flutter_screenutil.dart';
  7 +import 'package:wow_english/common/extension/string_extension.dart';
6 import 'package:wow_english/common/request/dao/lesson_dao.dart'; 8 import 'package:wow_english/common/request/dao/lesson_dao.dart';
7 import 'package:wow_english/common/request/exception.dart'; 9 import 'package:wow_english/common/request/exception.dart';
8 import 'package:wow_english/common/request/dao/listen_dao.dart'; 10 import 'package:wow_english/common/request/dao/listen_dao.dart';
9 import 'package:wow_english/models/course_process_entity.dart'; 11 import 'package:wow_english/models/course_process_entity.dart';
  12 +import 'package:wow_english/utils/audio_player_util.dart';
10 import 'package:wow_english/utils/loading.dart'; 13 import 'package:wow_english/utils/loading.dart';
11 import 'package:wow_english/utils/toast_util.dart'; 14 import 'package:wow_english/utils/toast_util.dart';
12 15
@@ -61,16 +64,20 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -61,16 +64,20 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
61 on<RequestEnterClassEvent>(_requestEnterClass); 64 on<RequestEnterClassEvent>(_requestEnterClass);
62 on<RequestVideoLessonEvent>(_requestVideoLesson); 65 on<RequestVideoLessonEvent>(_requestVideoLesson);
63 on<CurrentUnitIndexChangeEvent>(_pageControllerChange); 66 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
  67 + on<InitEvent>((event, emit) async {
  68 + await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe);
  69 + });
64 } 70 }
65 71
66 void _requestSectionsData( 72 void _requestSectionsData(
67 RequestDataEvent event, Emitter<SectionState> emitter) async { 73 RequestDataEvent event, Emitter<SectionState> emitter) async {
68 try { 74 try {
69 await loading(() async { 75 await loading(() async {
70 - List<CourseSectionEntity>? courseSectionEntities = await LessonDao.courseSection(courseUnitId: event.courseUnitId); 76 + List<CourseSectionEntity>? courseSectionEntities =
  77 + await LessonDao.courseSection(courseUnitId: event.courseUnitId);
71 if (courseSectionEntities != null) { 78 if (courseSectionEntities != null) {
72 _courseSectionDatasMap[event.courseUnitId] = 79 _courseSectionDatasMap[event.courseUnitId] =
73 - await LessonDao.courseSection(courseUnitId: event.courseUnitId); 80 + await LessonDao.courseSection(courseUnitId: event.courseUnitId);
74 emitter(LessonDataLoadState()); 81 emitter(LessonDataLoadState());
75 } 82 }
76 }); 83 });
@@ -224,7 +231,8 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -224,7 +231,8 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
224 ///查找当前unit的下一个section 231 ///查找当前unit的下一个section
225 CourseSectionEntity? nextCourseSectionEntity = 232 CourseSectionEntity? nextCourseSectionEntity =
226 findCourseSectionBySort(curSectionSort + 1); 233 findCourseSectionBySort(curSectionSort + 1);
227 - return checkCourseSectionLocked(courseLessonId, nextCourseSectionEntity, emitter); 234 + return checkCourseSectionLocked(
  235 + courseLessonId, nextCourseSectionEntity, emitter);
228 } catch (e) { 236 } catch (e) {
229 if (e is ApiException) { 237 if (e is ApiException) {
230 showToast(e.message.toString()); 238 showToast(e.message.toString());
@@ -234,7 +242,9 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -234,7 +242,9 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
234 } 242 }
235 243
236 ///检查section是否锁定 244 ///检查section是否锁定
237 - Future<CourseSectionEntity?> checkCourseSectionLocked(int courseLessonId, CourseSectionEntity? courseSectionEntity, 245 + Future<CourseSectionEntity?> checkCourseSectionLocked(
  246 + int courseLessonId,
  247 + CourseSectionEntity? courseSectionEntity,
238 Emitter<SectionState> emitter) async { 248 Emitter<SectionState> emitter) async {
239 if (courseSectionEntity != null) { 249 if (courseSectionEntity != null) {
240 if (courseSectionEntity.lock == false) { 250 if (courseSectionEntity.lock == false) {
@@ -243,15 +253,15 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -243,15 +253,15 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
243 } else { 253 } else {
244 ///如果section锁了,请求当前unit下的section数据,查询解锁状态 254 ///如果section锁了,请求当前unit下的section数据,查询解锁状态
245 int courseUnitId = courseSectionEntity.courseUnitId; 255 int courseUnitId = courseSectionEntity.courseUnitId;
246 - CourseSectionEntity? result = await loading(() async { 256 + CourseSectionEntity? result = await loading(() async {
247 List<CourseSectionEntity>? tempSectionEntities = 257 List<CourseSectionEntity>? tempSectionEntities =
248 - await LessonDao.courseSection(courseUnitId: courseUnitId); 258 + await LessonDao.courseSection(courseUnitId: courseUnitId);
249 if (tempSectionEntities != null) { 259 if (tempSectionEntities != null) {
250 _courseSectionDatasMap[courseUnitId] = tempSectionEntities; 260 _courseSectionDatasMap[courseUnitId] = tempSectionEntities;
251 emitter(LessonDataLoadState()); 261 emitter(LessonDataLoadState());
252 } 262 }
253 courseSectionEntity = tempSectionEntities?.firstWhereOrNull( 263 courseSectionEntity = tempSectionEntities?.firstWhereOrNull(
254 - (element) => element.id == courseSectionEntity?.id); 264 + (element) => element.id == courseSectionEntity?.id);
255 if (courseSectionEntity?.lock == false) { 265 if (courseSectionEntity?.lock == false) {
256 ///刷新后的数据如果解锁了,直接返回 266 ///刷新后的数据如果解锁了,直接返回
257 return courseSectionEntity; 267 return courseSectionEntity;
@@ -270,18 +280,20 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -270,18 +280,20 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
270 if (curCourseUnitDetail != null) { 280 if (curCourseUnitDetail != null) {
271 ///再根据当前unit的sortOrder找出下一个unit 281 ///再根据当前unit的sortOrder找出下一个unit
272 CourseUnitDetail? nextCourseUnitDetail = 282 CourseUnitDetail? nextCourseUnitDetail =
273 - _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) =>  
274 - element.sortOrder == (curCourseUnitDetail.sortOrder! + 1)); 283 + _courseUnitEntity.courseUnitVOList?.firstWhereOrNull((element) =>
  284 + element.sortOrder == (curCourseUnitDetail.sortOrder! + 1));
275 285
276 if (nextCourseUnitDetail != null) { 286 if (nextCourseUnitDetail != null) {
277 if (nextCourseUnitDetail.lock == true) { 287 if (nextCourseUnitDetail.lock == true) {
278 ///如果下一个unit是锁定状态,请求数据刷新查询解锁状态 288 ///如果下一个unit是锁定状态,请求数据刷新查询解锁状态
279 CourseSectionEntity? result = await loading(() async { 289 CourseSectionEntity? result = await loading(() async {
280 - CourseUnitEntity? newCourseUnitEntity = await LessonDao.courseUnit(  
281 - _courseUnitEntity.nowCourseModuleId); 290 + CourseUnitEntity? newCourseUnitEntity =
  291 + await LessonDao.courseUnit(
  292 + _courseUnitEntity.nowCourseModuleId);
282 293
283 ///拿到重新获取到的unit后,再次判断是否解锁 294 ///拿到重新获取到的unit后,再次判断是否解锁
284 - nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList?.firstWhereOrNull( 295 + nextCourseUnitDetail = newCourseUnitEntity?.courseUnitVOList
  296 + ?.firstWhereOrNull(
285 (element) => element.id == nextCourseUnitDetail?.id); 297 (element) => element.id == nextCourseUnitDetail?.id);
286 if (nextCourseUnitDetail?.lock == false) { 298 if (nextCourseUnitDetail?.lock == false) {
287 ///解锁状态从锁定到解锁,覆盖原unit数据并刷新ui 299 ///解锁状态从锁定到解锁,覆盖原unit数据并刷新ui
@@ -289,7 +301,8 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -289,7 +301,8 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
289 courseUnitEntityChanged = true; 301 courseUnitEntityChanged = true;
290 emitter(LessonDataLoadState()); 302 emitter(LessonDataLoadState());
291 303
292 - return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail!.id!, emitter); 304 + return checkCourseSectionLockedOfNextUnit(
  305 + courseLessonId, nextCourseUnitDetail!.id!, emitter);
293 } else { 306 } else {
294 showToast('下个单元课程还没解锁哦'); 307 showToast('下个单元课程还没解锁哦');
295 308
@@ -299,10 +312,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -299,10 +312,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
299 }); 312 });
300 return result; 313 return result;
301 } else { 314 } else {
302 - return checkCourseSectionLockedOfNextUnit(courseLessonId, nextCourseUnitDetail.id!, emitter); 315 + return checkCourseSectionLockedOfNextUnit(
  316 + courseLessonId, nextCourseUnitDetail.id!, emitter);
303 } 317 }
304 } else { 318 } else {
305 showToast("恭喜你,本阶段学到顶啦"); 319 showToast("恭喜你,本阶段学到顶啦");
  320 +
306 ///最后一个unit了 321 ///最后一个unit了
307 return null; 322 return null;
308 } 323 }
@@ -314,13 +329,16 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -314,13 +329,16 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
314 } 329 }
315 330
316 ///检查下一个unit的(第一个)section 331 ///检查下一个unit的(第一个)section
317 - Future<CourseSectionEntity?> checkCourseSectionLockedOfNextUnit(int courseLessonId, int nextCourseUnitDetailId, 332 + Future<CourseSectionEntity?> checkCourseSectionLockedOfNextUnit(
  333 + int courseLessonId,
  334 + int nextCourseUnitDetailId,
318 Emitter<SectionState> emitter) async { 335 Emitter<SectionState> emitter) async {
319 - CourseSectionEntity? firstSectionNextUnit = await getFirstSectionByUnitId(  
320 - nextCourseUnitDetailId, emitter); 336 + CourseSectionEntity? firstSectionNextUnit =
  337 + await getFirstSectionByUnitId(nextCourseUnitDetailId, emitter);
321 if (firstSectionNextUnit != null) { 338 if (firstSectionNextUnit != null) {
322 ///下个unit的第一个section如果不为空,再次检查是否锁定 339 ///下个unit的第一个section如果不为空,再次检查是否锁定
323 - CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked(courseLessonId, firstSectionNextUnit, emitter); 340 + CourseSectionEntity? courseSectionEntity = await checkCourseSectionLocked(
  341 + courseLessonId, firstSectionNextUnit, emitter);
324 if (courseSectionEntity != null) { 342 if (courseSectionEntity != null) {
325 ///只有是下一unit的第一个section并且解锁了,才跳转 343 ///只有是下一unit的第一个section并且解锁了,才跳转
326 _pageController.nextPage( 344 _pageController.nextPage(
lib/pages/section/bloc/section_event.dart
@@ -9,6 +9,8 @@ class RequestDataEvent extends SectionEvent { @@ -9,6 +9,8 @@ class RequestDataEvent extends SectionEvent {
9 RequestDataEvent(this.courseUnitId); 9 RequestDataEvent(this.courseUnitId);
10 } 10 }
11 11
  12 +class InitEvent extends SectionEvent {}
  13 +
12 ///获取视频课程内容 14 ///获取视频课程内容
13 class RequestVideoLessonEvent extends SectionEvent { 15 class RequestVideoLessonEvent extends SectionEvent {
14 final String courseLessonId; 16 final String courseLessonId;
lib/pages/section/section_page.dart
  1 +import 'package:audioplayers/audioplayers.dart';
1 import 'package:flutter/cupertino.dart'; 2 import 'package:flutter/cupertino.dart';
2 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
3 import 'package:flutter_bloc/flutter_bloc.dart'; 4 import 'package:flutter_bloc/flutter_bloc.dart';
@@ -11,6 +12,8 @@ import &#39;package:wow_english/pages/section/widgets/section_item.dart&#39;; @@ -11,6 +12,8 @@ import &#39;package:wow_english/pages/section/widgets/section_item.dart&#39;;
11 import 'package:wow_english/pages/section/widgets/section_bouns_item.dart'; 12 import 'package:wow_english/pages/section/widgets/section_bouns_item.dart';
12 import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; 13 import 'package:wow_english/pages/section/widgets/section_header_widget.dart';
13 import 'package:wow_english/route/route.dart'; 14 import 'package:wow_english/route/route.dart';
  15 +import 'package:wow_english/utils/audio_player_util.dart';
  16 +import 'package:wow_english/utils/log_util.dart';
14 import 'package:wow_english/utils/toast_util.dart'; 17 import 'package:wow_english/utils/toast_util.dart';
15 18
16 import '../../models/course_section_entity.dart'; 19 import '../../models/course_section_entity.dart';
@@ -38,7 +41,8 @@ class SectionPage extends StatelessWidget { @@ -38,7 +41,8 @@ class SectionPage extends StatelessWidget {
38 initialPage, 41 initialPage,
39 PageController(initialPage: initialPage), 42 PageController(initialPage: initialPage),
40 ScrollController(), 43 ScrollController(),
41 - ScrollController()), 44 + ScrollController())
  45 + ..add(InitEvent()),
42 //为了触发指示器进入后计算位置 46 //为了触发指示器进入后计算位置
43 // ..add(CurrentUnitIndexChangeEvent(initialPage)), 47 // ..add(CurrentUnitIndexChangeEvent(initialPage)),
44 child: _SectionPageView(context), 48 child: _SectionPageView(context),
@@ -55,7 +59,7 @@ class _SectionPageView extends StatelessWidget { @@ -55,7 +59,7 @@ class _SectionPageView extends StatelessWidget {
55 Widget build(BuildContext context) { 59 Widget build(BuildContext context) {
56 final bloc = BlocProvider.of<SectionBloc>(context); 60 final bloc = BlocProvider.of<SectionBloc>(context);
57 return BlocListener<SectionBloc, SectionState>( 61 return BlocListener<SectionBloc, SectionState>(
58 - listener: (context, state) { 62 + listener: (context, state) async {
59 if (state is RequestVideoLessonState) { 63 if (state is RequestVideoLessonState) {
60 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; 64 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? '';
61 var title = ''; 65 var title = '';
@@ -87,6 +91,8 @@ class _SectionPageView extends StatelessWidget { @@ -87,6 +91,8 @@ class _SectionPageView extends StatelessWidget {
87 currentTime: dataMap['currentTime'], 91 currentTime: dataMap['currentTime'],
88 autoNextSection: dataMap['nextSection'])); 92 autoNextSection: dataMap['nextSection']));
89 } 93 }
  94 + AudioPlayerUtil.getInstance()
  95 + .playAudio(AudioPlayerUtilType.countWithMe);
90 }); 96 });
91 return; 97 return;
92 } 98 }
@@ -96,12 +102,22 @@ class _SectionPageView extends StatelessWidget { @@ -96,12 +102,22 @@ class _SectionPageView extends StatelessWidget {
96 state.courseType != SectionType.pictureBook.value) { 102 state.courseType != SectionType.pictureBook.value) {
97 ///视频类型 103 ///视频类型
98 ///获取视频课程内容 104 ///获取视频课程内容
  105 + if (state.courseType == 1) {
  106 + await AudioPlayerUtil.getInstance()
  107 + .playAudio(AudioPlayerUtilType.musicTime);
  108 + } else {
  109 + await AudioPlayerUtil.getInstance()
  110 + .playAudio(AudioPlayerUtilType.videoTime);
  111 + }
99 bloc.add(RequestVideoLessonEvent( 112 bloc.add(RequestVideoLessonEvent(
100 state.courseLessonId, state.courseType)); 113 state.courseLessonId, state.courseType));
  114 +
101 return; 115 return;
102 } 116 }
103 117
104 if (state.courseType == SectionType.pictureBook.value) { 118 if (state.courseType == SectionType.pictureBook.value) {
  119 + await AudioPlayerUtil.getInstance()
  120 + .playAudio(AudioPlayerUtilType.readingTime);
105 //绘本 121 //绘本
106 pushNamed(AppRouteName.reading, 122 pushNamed(AppRouteName.reading,
107 arguments: {'courseLessonId': state.courseLessonId}) 123 arguments: {'courseLessonId': state.courseLessonId})
@@ -114,13 +130,18 @@ class _SectionPageView extends StatelessWidget { @@ -114,13 +130,18 @@ class _SectionPageView extends StatelessWidget {
114 currentStep: dataMap['currentStep'], 130 currentStep: dataMap['currentStep'],
115 autoNextSection: dataMap['nextSection'], 131 autoNextSection: dataMap['nextSection'],
116 )); 132 ));
  133 + AudioPlayerUtil.getInstance()
  134 + .playAudio(AudioPlayerUtilType.countWithMe);
117 } 135 }
118 }); 136 });
  137 +
119 return; 138 return;
120 } 139 }
121 140
122 if (state.courseType == SectionType.practice.value) { 141 if (state.courseType == SectionType.practice.value) {
123 //练习 142 //练习
  143 + await AudioPlayerUtil.getInstance()
  144 + .playAudio(AudioPlayerUtilType.quizTime);
124 pushNamed(AppRouteName.topicPic, 145 pushNamed(AppRouteName.topicPic,
125 arguments: {'courseLessonId': state.courseLessonId}) 146 arguments: {'courseLessonId': state.courseLessonId})
126 .then((value) { 147 .then((value) {
@@ -131,6 +152,8 @@ class _SectionPageView extends StatelessWidget { @@ -131,6 +152,8 @@ class _SectionPageView extends StatelessWidget {
131 currentStep: dataMap['currentStep'], 152 currentStep: dataMap['currentStep'],
132 autoNextSection: dataMap['nextSection'])); 153 autoNextSection: dataMap['nextSection']));
133 } 154 }
  155 + AudioPlayerUtil.getInstance()
  156 + .playAudio(AudioPlayerUtilType.countWithMe);
134 }); 157 });
135 return; 158 return;
136 } 159 }
lib/pages/shop/home/shop_desc_page.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +import 'package:wow_english/common/widgets/we_app_bar.dart';
  5 +
  6 +///购前须知页
  7 +class ShopDescPage extends StatelessWidget {
  8 + const ShopDescPage({super.key});
  9 +
  10 + @override
  11 + Widget build(BuildContext context) {
  12 + return _ShopDescPageView();
  13 + }
  14 +}
  15 +
  16 +class _ShopDescPageView extends StatelessWidget {
  17 +
  18 + @override
  19 + Widget build(BuildContext context) {
  20 + return Scaffold(
  21 + appBar: const WEAppBar(
  22 + titleText: '购前须知',
  23 + centerTitle: true,
  24 + ),
  25 + body: SingleChildScrollView(
  26 + child: Center(
  27 + child: Image.asset('shop_desc'.assetPng),
  28 + ),
  29 + ),
  30 + );
  31 + }
  32 +}
lib/pages/shop/home/shop_home_page.dart
@@ -58,7 +58,7 @@ class _ShopHomeView extends StatelessWidget { @@ -58,7 +58,7 @@ class _ShopHomeView extends StatelessWidget {
58 ), 58 ),
59 color: Colors.white, 59 color: Colors.white,
60 onPressed: () { 60 onPressed: () {
61 - showToast('购前须知'); 61 + pushNamed(AppRouteName.shopDesc);
62 }, 62 },
63 ) 63 )
64 ], 64 ],
lib/pages/unit/bloc.dart
1 import 'package:bloc/bloc.dart'; 1 import 'package:bloc/bloc.dart';
2 import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; 2 import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart';
  3 +import 'package:wow_english/utils/audio_player_util.dart';
3 4
4 import '../../common/request/dao/lesson_dao.dart'; 5 import '../../common/request/dao/lesson_dao.dart';
5 import '../../common/request/exception.dart'; 6 import '../../common/request/exception.dart';
@@ -24,6 +25,9 @@ class UnitBloc extends Bloc&lt;UnitEvent, UnitState&gt; { @@ -24,6 +25,9 @@ class UnitBloc extends Bloc&lt;UnitEvent, UnitState&gt; {
24 25
25 UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) { 26 UnitBloc(CourseModuleEntity? courseEntity) : super(UnitState().init()) {
26 on<RequestUnitDataEvent>(_requestUnitDatas); 27 on<RequestUnitDataEvent>(_requestUnitDatas);
  28 + on<UnitInitEvent>((event, emit) {
  29 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.inMyTummy);
  30 + });
27 } 31 }
28 32
29 void _requestUnitDatas( 33 void _requestUnitDatas(
@@ -44,16 +48,28 @@ class UnitBloc extends Bloc&lt;UnitEvent, UnitState&gt; { @@ -44,16 +48,28 @@ class UnitBloc extends Bloc&lt;UnitEvent, UnitState&gt; {
44 return _moduleEntity?.code ?? _unitData?.courseModuleCode; 48 return _moduleEntity?.code ?? _unitData?.courseModuleCode;
45 } 49 }
46 50
47 - void headerActionEvent(HeaderActionType type) { 51 + Future<void> headerActionEvent(HeaderActionType type) async {
  52 + await AudioPlayerUtil.getInstance().pause();
48 if (type == HeaderActionType.video) { 53 if (type == HeaderActionType.video) {
  54 + //视频跟读暂时隐藏了
49 pushNamed(AppRouteName.reAfter); 55 pushNamed(AppRouteName.reAfter);
50 } else if (type == HeaderActionType.phase) { 56 } else if (type == HeaderActionType.phase) {
51 - pushNamed(AppRouteName.courseModule); 57 + pushNamed(AppRouteName.courseModule).then((value) => {
  58 + AudioPlayerUtil.getInstance()
  59 + .playAudio(AudioPlayerUtilType.inMyTummy)
  60 + });
  61 + ;
52 } else if (type == HeaderActionType.listen) { 62 } else if (type == HeaderActionType.listen) {
53 - pushNamed(AppRouteName.listen); 63 + pushNamed(AppRouteName.listen).then((value) => {
  64 + AudioPlayerUtil.getInstance()
  65 + .playAudio(AudioPlayerUtilType.inMyTummy)
  66 + });
54 } else if (type == HeaderActionType.shop) { 67 } else if (type == HeaderActionType.shop) {
55 - pushNamed(AppRouteName.shop)  
56 - .then((value) => {exchangeResult = value['exchange']}); 68 + pushNamed(AppRouteName.shop).then((value) => {
  69 + AudioPlayerUtil.getInstance()
  70 + .playAudio(AudioPlayerUtilType.inMyTummy),
  71 + exchangeResult = value['exchange']
  72 + });
57 } else if (type == HeaderActionType.user) { 73 } else if (type == HeaderActionType.user) {
58 pushNamed(AppRouteName.user); 74 pushNamed(AppRouteName.user);
59 } 75 }
lib/pages/unit/event.dart
@@ -6,3 +6,5 @@ class RequestUnitDataEvent extends UnitEvent { @@ -6,3 +6,5 @@ class RequestUnitDataEvent extends UnitEvent {
6 6
7 RequestUnitDataEvent(this.moduleId); 7 RequestUnitDataEvent(this.moduleId);
8 } 8 }
  9 +
  10 +class UnitInitEvent extends UnitEvent {}
lib/pages/unit/view.dart
@@ -5,6 +5,7 @@ import &#39;package:wow_english/pages/unit/state.dart&#39;; @@ -5,6 +5,7 @@ import &#39;package:wow_english/pages/unit/state.dart&#39;;
5 import 'package:wow_english/pages/unit/widget/course_unit_item.dart'; 5 import 'package:wow_english/pages/unit/widget/course_unit_item.dart';
6 import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart'; 6 import 'package:wow_english/pages/unit/widget/home_tab_header_widget.dart';
7 import 'package:wow_english/route/route.dart'; 7 import 'package:wow_english/route/route.dart';
  8 +import 'package:wow_english/utils/audio_player_util.dart';
8 9
9 import '../../models/course_module_entity.dart'; 10 import '../../models/course_module_entity.dart';
10 import '../../models/course_unit_entity.dart'; 11 import '../../models/course_unit_entity.dart';
@@ -23,6 +24,7 @@ class UnitPage extends StatelessWidget { @@ -23,6 +24,7 @@ class UnitPage extends StatelessWidget {
23 Widget build(BuildContext context) { 24 Widget build(BuildContext context) {
24 return BlocProvider( 25 return BlocProvider(
25 create: (BuildContext context) => UnitBloc(courseModuleEntity) 26 create: (BuildContext context) => UnitBloc(courseModuleEntity)
  27 + ..add(UnitInitEvent())
26 ..add(RequestUnitDataEvent(courseModuleEntity?.id)), 28 ..add(RequestUnitDataEvent(courseModuleEntity?.id)),
27 child: Builder(builder: (context) => _buildPage(context)), 29 child: Builder(builder: (context) => _buildPage(context)),
28 ); 30 );
@@ -41,6 +43,8 @@ class UnitPage extends StatelessWidget { @@ -41,6 +43,8 @@ class UnitPage extends StatelessWidget {
41 HomeTabHeaderWidget( 43 HomeTabHeaderWidget(
42 courseModuleCode: bloc.getCourseModuleCode(), 44 courseModuleCode: bloc.getCourseModuleCode(),
43 onBack: () { 45 onBack: () {
  46 + AudioPlayerUtil.getInstance()
  47 + .playAudio(AudioPlayerUtilType.touch);
44 popPage(data: {'exchange': bloc.exchangeResult}); 48 popPage(data: {'exchange': bloc.exchangeResult});
45 }, 49 },
46 actionTap: (HeaderActionType type) { 50 actionTap: (HeaderActionType type) {
@@ -58,17 +62,19 @@ class UnitPage extends StatelessWidget { @@ -58,17 +62,19 @@ class UnitPage extends StatelessWidget {
58 CourseUnitDetail? data = 62 CourseUnitDetail? data =
59 bloc.unitData?.courseUnitVOList?[index]; 63 bloc.unitData?.courseUnitVOList?[index];
60 return GestureDetector( 64 return GestureDetector(
61 - onTap: () { 65 + onTap: () async {
62 if (data.lock == true) { 66 if (data.lock == true) {
63 showToast('当前单元课程暂未解锁'); 67 showToast('当前单元课程暂未解锁');
64 return; 68 return;
65 } 69 }
66 - 70 + // await AudioPlayerUtil.getInstance().pause();
67 pushNamed(AppRouteName.courseSection, 71 pushNamed(AppRouteName.courseSection,
68 arguments: { 72 arguments: {
69 'courseUnitEntity': bloc.unitData, 73 'courseUnitEntity': bloc.unitData,
70 'courseUnitId': data.id 74 'courseUnitId': data.id
71 }).then((value) { 75 }).then((value) {
  76 + AudioPlayerUtil.getInstance()
  77 + .playAudio(AudioPlayerUtilType.inMyTummy);
72 if (value != null) { 78 if (value != null) {
73 Map<String, dynamic> dataMap = 79 Map<String, dynamic> dataMap =
74 value as Map<String, dynamic>; 80 value as Map<String, dynamic>;
lib/pages/unit/widget/course_unit_item.dart
@@ -17,14 +17,13 @@ class CourseUnitItem extends StatelessWidget { @@ -17,14 +17,13 @@ class CourseUnitItem extends StatelessWidget {
17 @override 17 @override
18 Widget build(BuildContext context) { 18 Widget build(BuildContext context) {
19 return Padding( 19 return Padding(
20 - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 24.h),  
21 - child: Stack(  
22 - children: [  
23 - _normalItem(),  
24 - _lockWidget(),  
25 - ],  
26 - )  
27 - ); 20 + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 24.h),
  21 + child: Stack(
  22 + children: [
  23 + _normalItem(),
  24 + _lockWidget(),
  25 + ],
  26 + ));
28 } 27 }
29 28
30 Widget _normalItem() { 29 Widget _normalItem() {
@@ -40,17 +39,17 @@ class CourseUnitItem extends StatelessWidget { @@ -40,17 +39,17 @@ class CourseUnitItem extends StatelessWidget {
40 children: [ 39 children: [
41 Expanded( 40 Expanded(
42 child: Container( 41 child: Container(
43 - decoration: BoxDecoration(  
44 - border: Border.all(  
45 - width: 2,  
46 - color: const Color(0xFF140C10),  
47 - ),  
48 - borderRadius: BorderRadius.circular(6)),  
49 - child: OwImageWidget(  
50 - name: unitLesson.coverUrl ?? '',  
51 - fit: BoxFit.fitHeight, 42 + decoration: BoxDecoration(
  43 + border: Border.all(
  44 + width: 2,
  45 + color: const Color(0xFF140C10),
52 ), 46 ),
53 - )), 47 + borderRadius: BorderRadius.circular(6)),
  48 + child: OwImageWidget(
  49 + name: unitLesson.coverUrl ?? '',
  50 + fit: BoxFit.fitHeight,
  51 + ),
  52 + )),
54 20.verticalSpace, 53 20.verticalSpace,
55 SizedBox( 54 SizedBox(
56 height: 40.h, 55 height: 40.h,
@@ -58,8 +57,7 @@ class CourseUnitItem extends StatelessWidget { @@ -58,8 +57,7 @@ class CourseUnitItem extends StatelessWidget {
58 unitLesson.name ?? '', 57 unitLesson.name ?? '',
59 maxLines: 2, 58 maxLines: 2,
60 overflow: TextOverflow.ellipsis, 59 overflow: TextOverflow.ellipsis,
61 - style:  
62 - TextStyle(fontSize: 11.sp, color: const Color(0xFF140C10)), 60 + style: TextStyle(fontSize: 11.sp, color: const Color(0xFF140C10)),
63 ), 61 ),
64 ) 62 )
65 ], 63 ],
@@ -75,12 +73,8 @@ class CourseUnitItem extends StatelessWidget { @@ -75,12 +73,8 @@ class CourseUnitItem extends StatelessWidget {
75 width: 165.w, 73 width: 165.w,
76 decoration: BoxDecoration( 74 decoration: BoxDecoration(
77 image: DecorationImage( 75 image: DecorationImage(
78 - image: AssetImage(  
79 - 'gendubeij_mengban'.assetPng  
80 - ),  
81 - fit: BoxFit.fill  
82 - )  
83 - ), 76 + image: AssetImage('gendubeij_mengban'.assetPng),
  77 + fit: BoxFit.fill)),
84 alignment: Alignment.center, 78 alignment: Alignment.center,
85 child: Image.asset( 79 child: Image.asset(
86 'iv_lock'.assetPng, 80 'iv_lock'.assetPng,
lib/pages/user/setting/reback_page.dart
@@ -26,73 +26,87 @@ class ReBackPageState extends State&lt;ReBackPage&gt; { @@ -26,73 +26,87 @@ class ReBackPageState extends State&lt;ReBackPage&gt; {
26 @override 26 @override
27 Widget build(BuildContext context) { 27 Widget build(BuildContext context) {
28 return Scaffold( 28 return Scaffold(
29 - appBar: const WEAppBar(  
30 - titleText: '我要反馈',  
31 - ),  
32 - body: Container(  
33 - color: Colors.white,  
34 - padding: EdgeInsets.symmetric(  
35 - horizontal: 24.w 29 + appBar: const WEAppBar(
  30 + titleText: '我要反馈',
36 ), 31 ),
37 - child: SafeArea(  
38 - child: Column(  
39 - children: [  
40 - 20.verticalSpace,  
41 - Row(  
42 - mainAxisAlignment: MainAxisAlignment.spaceBetween,  
43 - children: [  
44 - Text(  
45 - '请输入您要反馈的问题和意见,10-500个字',  
46 - textAlign: TextAlign.left,  
47 - style: TextStyle(  
48 - fontSize: 19.sp,  
49 - color: HexColor('#333333')  
50 - ),  
51 - ),  
52 - Text(  
53 - '48/500',  
54 - textAlign: TextAlign.right,  
55 - style: TextStyle(  
56 - fontSize: 19.sp,  
57 - color: HexColor('#333333')  
58 - ),)  
59 - ],  
60 - ),  
61 - 9.5.verticalSpace,  
62 - Expanded(  
63 - child: Container(  
64 - decoration: BoxDecoration(  
65 - image: DecorationImage(  
66 - fit: BoxFit.fill,  
67 - image: AssetImage('bg_reback'.assetPng) 32 + body: Container(
  33 + color: Colors.white,
  34 + padding: EdgeInsets.symmetric(horizontal: 10.w),
  35 + child: SafeArea(
  36 + child: LayoutBuilder(builder: (context, constraints) {
  37 + return SingleChildScrollView(
  38 + child: ConstrainedBox(
  39 + constraints: BoxConstraints(
  40 + minHeight: constraints.maxHeight,
  41 + ),
  42 + child: IntrinsicHeight(
  43 + child: Column(
  44 + children: [
  45 + 20.verticalSpace,
  46 + Row(
  47 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
  48 + children: [
  49 + Text(
  50 + '请输入您要反馈的问题和意见,10-500个字',
  51 + textAlign: TextAlign.left,
  52 + style: TextStyle(
  53 + fontSize: 19.sp, color: HexColor('#333333')),
  54 + ),
  55 + Text(
  56 + '48/500',
  57 + textAlign: TextAlign.right,
  58 + style: TextStyle(
  59 + fontSize: 19.sp, color: HexColor('#333333')),
  60 + )
  61 + ],
  62 + ),
  63 + 9.5.verticalSpace,
  64 + Expanded(
  65 + child: Container(
  66 + decoration: BoxDecoration(
  67 + image: DecorationImage(
  68 + image: AssetImage('bg_reback'.assetPng),
  69 + fit: BoxFit.fill)),
  70 + child: Padding(
  71 + padding: const EdgeInsets.symmetric(
  72 + vertical: 10, horizontal: 16),
  73 + // 设置对称内边距
  74 + child: TextField(
  75 + textInputAction: TextInputAction.done,
  76 + decoration: InputDecoration(
  77 + border: InputBorder.none,
  78 + hintStyle: TextStyle(
  79 + fontSize: 16.sp,
  80 + color: const Color(0xFF999999))),
  81 + ),
  82 + ),
  83 + ),
  84 + ),
  85 + 4.5.verticalSpace,
  86 + Container(
  87 + decoration: BoxDecoration(
  88 + image: DecorationImage(
  89 + fit: BoxFit.fill,
  90 + image: AssetImage(_canEnsure
  91 + ? 're_button'.assetPng
  92 + : 're_button_dis'.assetPng))),
  93 + alignment: Alignment.center,
  94 + width: 91.w,
  95 + height: 45.h,
  96 + child: Text(
  97 + '提交',
  98 + textAlign: TextAlign.center,
  99 + style:
  100 + TextStyle(color: Colors.white, fontSize: 17.sp),
  101 + ),
68 ) 102 )
  103 + ],
69 ), 104 ),
70 ), 105 ),
71 ), 106 ),
72 - 4.5.verticalSpace,  
73 - Container(  
74 - decoration: BoxDecoration(  
75 - image: DecorationImage(  
76 - fit: BoxFit.fill,  
77 - image: AssetImage(_canEnsure?'re_button'.assetPng:'re_button_dis'.assetPng)  
78 - )  
79 - ),  
80 - alignment: Alignment.center,  
81 - width: 91.w,  
82 - height: 45.h,  
83 - child: Text(  
84 - '提交',  
85 - textAlign: TextAlign.center,  
86 - style: TextStyle(  
87 - color: Colors.white,  
88 - fontSize: 17.sp  
89 - ),  
90 - ),  
91 - )  
92 - ],  
93 - ), 107 + );
  108 + }),
94 ), 109 ),
95 - )  
96 - ); 110 + ));
97 } 111 }
98 -}  
99 \ No newline at end of file 112 \ No newline at end of file
  113 +}
lib/pages/user/setting/setting_page.dart
1 import 'package:flutter/material.dart'; 1 import 'package:flutter/material.dart';
2 import 'package:flutter_screenutil/flutter_screenutil.dart'; 2 import 'package:flutter_screenutil/flutter_screenutil.dart';
  3 +import 'package:package_info_plus/package_info_plus.dart';
3 import 'package:wow_english/common/widgets/we_app_bar.dart'; 4 import 'package:wow_english/common/widgets/we_app_bar.dart';
4 5
5 import '../../../route/route.dart'; 6 import '../../../route/route.dart';
@@ -9,14 +10,30 @@ class SettingPage extends StatefulWidget { @@ -9,14 +10,30 @@ class SettingPage extends StatefulWidget {
9 10
10 @override 11 @override
11 State<StatefulWidget> createState() { 12 State<StatefulWidget> createState() {
12 - return SettingPageState(); 13 + return SettingPageState();
13 } 14 }
14 } 15 }
15 16
16 class SettingPageState extends State<SettingPage> { 17 class SettingPageState extends State<SettingPage> {
  18 + String? _version;
  19 + String? _buildNum;
  20 + @override
  21 + void initState() {
  22 + super.initState();
  23 + _retrieveVersionInfo();
  24 + }
  25 +
  26 + Future<void> _retrieveVersionInfo() async {
  27 + PackageInfo packageInfo = await PackageInfo.fromPlatform();
  28 + setState(() {
  29 + _version = packageInfo.version;
  30 + _buildNum = packageInfo.buildNumber;
  31 + });
  32 + }
  33 +
17 @override 34 @override
18 Widget build(BuildContext context) { 35 Widget build(BuildContext context) {
19 - return Scaffold( 36 + return Scaffold(
20 appBar: const WEAppBar( 37 appBar: const WEAppBar(
21 titleText: '设置', 38 titleText: '设置',
22 ), 39 ),
@@ -28,17 +45,18 @@ class SettingPageState extends State&lt;SettingPage&gt; { @@ -28,17 +45,18 @@ class SettingPageState extends State&lt;SettingPage&gt; {
28 child: ListView( 45 child: ListView(
29 children: [ 46 children: [
30 34.verticalSpace, 47 34.verticalSpace,
31 - _buildItemWidget('注销账号', onPress: (){ 48 + _buildItemWidget('注销账号', onPress: () {
32 pushNamed(AppRouteName.deleteAccount); 49 pushNamed(AppRouteName.deleteAccount);
33 }), 50 }),
34 12.verticalSpace, 51 12.verticalSpace,
35 - _buildItemWidget('清除缓存', onPress: (){  
36 -  
37 - }), 52 + _buildItemWidget('清除缓存', onPress: () {}),
38 12.verticalSpace, 53 12.verticalSpace,
39 - _buildItemWidget('帮助与反馈', onPress: (){ 54 + _buildItemWidget('帮助与反馈', onPress: () {
40 pushNamed(AppRouteName.reBack); 55 pushNamed(AppRouteName.reBack);
41 }), 56 }),
  57 + 12.verticalSpace,
  58 + _buildItemWidget('Version: $_version Build:$_buildNum',
  59 + onPress: () {}),
42 ], 60 ],
43 ), 61 ),
44 ), 62 ),
@@ -46,13 +64,15 @@ class SettingPageState extends State&lt;SettingPage&gt; { @@ -46,13 +64,15 @@ class SettingPageState extends State&lt;SettingPage&gt; {
46 ), 64 ),
47 ); 65 );
48 } 66 }
49 -  
50 - Widget _buildItemWidget(String text,{VoidCallback? onPress}) { 67 +
  68 + Widget _buildItemWidget(String text, {VoidCallback? onPress}) {
51 return OutlinedButton( 69 return OutlinedButton(
52 onPressed: () => onPress?.call(), 70 onPressed: () => onPress?.call(),
53 style: ButtonStyle( 71 style: ButtonStyle(
54 - side: MaterialStateProperty.all(BorderSide(color: const Color(0xFF140C10), width: 1.5.w)),  
55 - shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))), 72 + side: MaterialStateProperty.all(
  73 + BorderSide(color: const Color(0xFF140C10), width: 1.5.w)),
  74 + shape: MaterialStateProperty.all(
  75 + RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.r))),
56 minimumSize: MaterialStateProperty.all(Size(double.infinity, 58.h)), 76 minimumSize: MaterialStateProperty.all(Size(double.infinity, 58.h)),
57 backgroundColor: MaterialStateProperty.all(Colors.white), 77 backgroundColor: MaterialStateProperty.all(Colors.white),
58 ), 78 ),
@@ -66,4 +86,4 @@ class SettingPageState extends State&lt;SettingPage&gt; { @@ -66,4 +86,4 @@ class SettingPageState extends State&lt;SettingPage&gt; {
66 ), 86 ),
67 ); 87 );
68 } 88 }
69 -}  
70 \ No newline at end of file 89 \ No newline at end of file
  90 +}
lib/pages/user/user_page.dart
@@ -173,15 +173,7 @@ class _UserView extends StatelessWidget { @@ -173,15 +173,7 @@ class _UserView extends StatelessWidget {
173 UserUtil.getUser()?.phoneNum == '17718485544') 173 UserUtil.getUser()?.phoneNum == '17718485544')
174 ? 12.verticalSpace 174 ? 12.verticalSpace
175 : 1.verticalSpace), 175 : 1.verticalSpace),
176 - OutlinedButton(  
177 - onPressed: () => pushNamed(AppRouteName.fogPwd),  
178 - style: normalButtonStyle,  
179 - child: Text(  
180 - "修改密码",  
181 - style: textStyle21sp,  
182 - ),  
183 - ),  
184 - 12.verticalSpace, 176 +
185 Offstage( 177 Offstage(
186 offstage: AppConfigHelper.shouldHidePay(), 178 offstage: AppConfigHelper.shouldHidePay(),
187 child: OutlinedButton( 179 child: OutlinedButton(
@@ -199,6 +191,15 @@ class _UserView extends StatelessWidget { @@ -199,6 +191,15 @@ class _UserView extends StatelessWidget {
199 style: textStyle21sp, 191 style: textStyle21sp,
200 )), 192 )),
201 ), 193 ),
  194 + 12.verticalSpace,
  195 + OutlinedButton(
  196 + onPressed: () => pushNamed(AppRouteName.fogPwd),
  197 + style: normalButtonStyle,
  198 + child: Text(
  199 + "修改密码",
  200 + style: textStyle21sp,
  201 + ),
  202 + ),
202 Offstage( 203 Offstage(
203 offstage: AppConfigHelper.shouldHidePay(), 204 offstage: AppConfigHelper.shouldHidePay(),
204 child: 12.verticalSpace, 205 child: 12.verticalSpace,
lib/route/route.dart
@@ -17,6 +17,7 @@ import &#39;package:wow_english/pages/repeatafter/repeat_after_page.dart&#39;; @@ -17,6 +17,7 @@ import &#39;package:wow_english/pages/repeatafter/repeat_after_page.dart&#39;;
17 import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart'; 17 import 'package:wow_english/pages/repeataftercontent/repeat_after_content_page.dart';
18 import 'package:wow_english/pages/shop/exchane/exchange_lesson_page.dart'; 18 import 'package:wow_english/pages/shop/exchane/exchange_lesson_page.dart';
19 import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.dart'; 19 import 'package:wow_english/pages/shop/exchangelist/exchange_lesson_list_page.dart';
  20 +import 'package:wow_english/pages/shop/home/shop_desc_page.dart';
20 import 'package:wow_english/pages/shop/home/shop_home_page.dart'; 21 import 'package:wow_english/pages/shop/home/shop_home_page.dart';
21 import 'package:wow_english/pages/user/information/user_information_page.dart'; 22 import 'package:wow_english/pages/user/information/user_information_page.dart';
22 import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart'; 23 import 'package:wow_english/pages/user/modify/modify_user_avatar_page.dart';
@@ -49,6 +50,7 @@ class AppRouteName { @@ -49,6 +50,7 @@ class AppRouteName {
49 static const String courseSection = 'courseSections'; 50 static const String courseSection = 'courseSections';
50 static const String listen = 'listen'; 51 static const String listen = 'listen';
51 static const String shop = 'shop'; 52 static const String shop = 'shop';
  53 + static const String shopDesc = 'shopDesc';
52 static const String exLesson = 'exLesson'; 54 static const String exLesson = 'exLesson';
53 static const String exList = 'exList'; 55 static const String exList = 'exList';
54 static const String reAfter = 'reAfter'; 56 static const String reAfter = 'reAfter';
@@ -141,6 +143,8 @@ class AppRouter { @@ -141,6 +143,8 @@ class AppRouter {
141 return CupertinoPageRoute(builder: (_) => const ListenPage()); 143 return CupertinoPageRoute(builder: (_) => const ListenPage());
142 case AppRouteName.shop: 144 case AppRouteName.shop:
143 return CupertinoPageRoute(builder: (_) => const ShopHomePage()); 145 return CupertinoPageRoute(builder: (_) => const ShopHomePage());
  146 + case AppRouteName.shopDesc:
  147 + return CupertinoPageRoute(builder: (_) => const ShopDescPage());
144 case AppRouteName.pay: 148 case AppRouteName.pay:
145 var productEntity = ProductEntity(); 149 var productEntity = ProductEntity();
146 if (settings.arguments != null && settings.arguments is ProductEntity) { 150 if (settings.arguments != null && settings.arguments is ProductEntity) {
lib/utils/audio_player_util.dart 0 → 100644
  1 +import 'package:audioplayers/audioplayers.dart';
  2 +import 'package:flutter/cupertino.dart';
  3 +import 'package:wow_english/common/extension/string_extension.dart';
  4 +
  5 +import 'log_util.dart';
  6 +
  7 +enum AudioPlayerUtilType {
  8 + welcomeToWow('welcome_to_wow'),
  9 + classTime('class_time'),
  10 + gameTime('game_time'),
  11 + musicTime('music_time'),
  12 + readingTime('reading_time'),
  13 + videoTime('video_time'),
  14 + quizTime('quiz_time'),
  15 + countWithMe('count_with_me_instrumental'),
  16 + inMyTummy('in_my_tummy_instrumental'),
  17 + touch('touch_instrumental');
  18 +
  19 + const AudioPlayerUtilType(this.path);
  20 +
  21 + final String path;
  22 +}
  23 +
  24 +class AudioPlayerUtil extends WidgetsBindingObserver {
  25 + static AudioPlayerUtil? _instance;
  26 + late AudioPlayer _audioPlayer;
  27 + late AudioPlayerUtilType currentType;
  28 + bool _wasPlaying = false;
  29 + static const TAG = "AudioPlayerUtil";
  30 +
  31 + // 私有构造函数
  32 + AudioPlayerUtil._internal() {
  33 + // 监听应用生命周期
  34 + WidgetsBinding.instance.addObserver(this);
  35 + _audioPlayer = AudioPlayer();
  36 + _audioPlayer.onPlayerStateChanged.listen((event) async {
  37 + if (event == PlayerState.completed) {
  38 + // 播放结束再次播放
  39 + if (currentType == AudioPlayerUtilType.inMyTummy) {
  40 + AudioPlayerUtil.getInstance()
  41 + .playAudio(AudioPlayerUtilType.inMyTummy);
  42 + }
  43 + if (currentType == AudioPlayerUtilType.countWithMe) {
  44 + AudioPlayerUtil.getInstance()
  45 + .playAudio(AudioPlayerUtilType.countWithMe);
  46 + }
  47 + if (currentType == AudioPlayerUtilType.welcomeToWow) {
  48 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.touch);
  49 + }
  50 + if (currentType == AudioPlayerUtilType.touch) {
  51 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.touch);
  52 + }
  53 + }
  54 + });
  55 + }
  56 +
  57 + static AudioPlayerUtil getInstance() {
  58 + _instance ??= AudioPlayerUtil._internal();
  59 + return _instance!;
  60 + }
  61 +
  62 +// 播放音频
  63 + Future<void> playAudio(AudioPlayerUtilType type) async {
  64 + Log.d("$TAG playAudio $type");
  65 + currentType = type;
  66 + String path = type.path;
  67 + await _audioPlayer.play(AssetSource(path.assetMp3), volume: 0.5);
  68 + await _audioPlayer.onPlayerComplete.first;
  69 + }
  70 +
  71 + // stop
  72 + Future<void> stop() async {
  73 + Log.d("$TAG stop _audioPlayer.state=${_audioPlayer.state}");
  74 + await _audioPlayer.stop();
  75 + }
  76 +
  77 + // pause
  78 + Future<void> pause() async {
  79 + Log.d("$TAG pause _audioPlayer.state=${_audioPlayer.state}");
  80 + if (_audioPlayer.state == PlayerState.playing) {
  81 + await _audioPlayer.pause();
  82 + }
  83 + }
  84 +
  85 + // resume
  86 + Future<void> resume() async {
  87 + Log.d("$TAG resume _audioPlayer.state=${_audioPlayer.state}");
  88 + if (_audioPlayer.state == PlayerState.paused) {
  89 + await _audioPlayer.resume();
  90 + }
  91 + }
  92 +
  93 + @override
  94 + void didChangeAppLifecycleState(AppLifecycleState state) async {
  95 + Log.d("$TAG didChangeAppLifecycleState appState=$state _wasPlaying=$_wasPlaying _audioPlayer.state=${_audioPlayer.state}");
  96 + if (state == AppLifecycleState.paused) {
  97 + if (_audioPlayer.state == PlayerState.playing) {
  98 + _wasPlaying = true;
  99 + await pause();
  100 + };
  101 + } else if (state == AppLifecycleState.resumed) {
  102 + if (_wasPlaying == true) {
  103 + _wasPlaying = false;
  104 + await resume();
  105 + }
  106 + }
  107 + }
  108 +
  109 + void dispose() {
  110 + Log.d("$TAG dispose _audioPlayer.state=${_audioPlayer.state}");
  111 + _audioPlayer.dispose();
  112 + WidgetsBinding.instance.removeObserver(this);
  113 + }
  114 +}