Commit 951eb853271dcf1ccf7aab05a9f790956246c46a

Authored by 吴启风
1 parent 13de08d7

feat:代码优化-星星等动效对话框不受返回关闭,能保证稳定关闭

lib/common/utils/show_star_reward_dialog.dart
1 1 import 'package:flutter/material.dart';
2 2  
  3 +import '../../utils/log_util.dart';
3 4 import '../widgets/cheer_reward_widget.dart';
4 5 import '../widgets/star_reward_widget.dart';
5 6  
6   -void showStarRewardDialog(BuildContext context, {
  7 +Future<void> showStarRewardDialog(
  8 + BuildContext context, {
7 9 double width = 357.5,
8 10 double height = 165,
9 11 int starCount = 3,
10   -}) {
11   - showDialog(
  12 + VoidCallback? onDismiss,
  13 +}) async {
  14 + ///showDialog 方法的返回值将是 Navigator.of(context).pop 传递给对话框的参数
  15 + await showDialog(
12 16 context: context,
13   - barrierDismissible: false, // 点击对话框外部不关闭对话框
  17 + barrierDismissible: false,
14 18 builder: (BuildContext context) {
15   - return Dialog(
16   - backgroundColor: Colors.transparent, // 设置对话框背景为透明
17   - insetPadding: const EdgeInsets.all(0), // 去除对话框的内边距
18   - child: StarRewardWidget(
19   - width: width,
20   - height: height,
21   - isPlaying: true,
22   - starCount: starCount,
23   - onAnimationEnd: () {
24   - Navigator.of(context).pop(); // 关闭对话框
25   - },
  19 + return PopScope(
  20 + canPop: false,
  21 + onPopInvoked: (didPop) {
  22 + Log.d('WQF showStarRewardDialog onPopInvoked didPop=$didPop');
  23 + },
  24 + child: Dialog(
  25 + backgroundColor: Colors.transparent, // 设置对话框背景为透明
  26 + insetPadding: const EdgeInsets.all(0), // 去除对话框的内边距
  27 + child: StarRewardWidget(
  28 + width: width,
  29 + height: height,
  30 + isPlaying: true,
  31 + starCount: starCount,
  32 + onAnimationEnd: () {
  33 + Navigator.of(context).pop(); // 关闭对话框
  34 + },
  35 + ),
26 36 ),
27 37 );
28 38 },
29 39 );
  40 + onDismiss?.call();
30 41 }
31 42  
32   -void showCheerRewardDialog(BuildContext context, {
  43 +Future<void> showCheerRewardDialog(
  44 + BuildContext context, {
33 45 required String lottieFile,
34 46 double width = 357.5,
35 47 double height = 165,
36   -}) {
37   - showDialog(
  48 + VoidCallback? onDismiss,
  49 +}) async {
  50 + await showDialog(
38 51 context: context,
39 52 barrierDismissible: false, // 点击对话框外部不关闭对话框
40 53 builder: (BuildContext context) {
41   - return Dialog(
42   - backgroundColor: Colors.transparent, // 设置对话框背景为透明
43   - insetPadding: const EdgeInsets.all(0), // 去除对话框的内边距
44   - child: CheerRewardWidget(
45   - lottieFile: lottieFile,
46   - width: width,
47   - height: height,
48   - isPlaying: true,
49   - onAnimationEnd: () {
50   - Navigator.of(context).pop(); // 关闭对话框
51   - },
  54 + return PopScope(
  55 + canPop: false,
  56 + onPopInvoked: (didPop) {
  57 + Log.d('WQF showCheerRewardDialog onPopInvoked didPop=$didPop');
  58 + },
  59 + child: Dialog(
  60 + backgroundColor: Colors.transparent, // 设置对话框背景为透明
  61 + insetPadding: const EdgeInsets.all(0), // 去除对话框的内边距
  62 + child: CheerRewardWidget(
  63 + lottieFile: lottieFile,
  64 + width: width,
  65 + height: height,
  66 + isPlaying: true,
  67 + onAnimationEnd: () {
  68 + Navigator.of(context).pop(); // 关闭对话框
  69 + },
  70 + ),
52 71 ),
53 72 );
54 73 },
55 74 );
56   -}
57 75 \ No newline at end of file
  76 + onDismiss?.call();
  77 +}
... ...
lib/common/widgets/cheer_reward_widget.dart
... ... @@ -53,6 +53,10 @@ class _CheerRewardWidgetState extends State&lt;CheerRewardWidget&gt;
53 53 }
54 54 }
55 55  
  56 + Future<LottieComposition> _loadLottieComposition() async {
  57 + return AssetLottie(widget.lottieFile).load();
  58 + }
  59 +
56 60 void _startAnimation() {
57 61 Log.d("$TAG ${identityHashCode(this)} _startAnimation");
58 62 setState(() {
... ... @@ -67,16 +71,12 @@ class _CheerRewardWidgetState extends State&lt;CheerRewardWidget&gt;
67 71 setState(() {
68 72 _isVisible = false;
69 73 });
70   - widget.onAnimationEnd(); // 调用外部回调函数
71 74 }
  75 + widget.onAnimationEnd(); // 调用外部回调函数
72 76 });
73 77 });
74 78 }
75 79  
76   - Future<LottieComposition> _loadLottieComposition() async {
77   - return AssetLottie(widget.lottieFile).load();
78   - }
79   -
80 80 @override
81 81 void dispose() {
82 82 _controller.dispose();
... ...
lib/common/widgets/star_reward_widget.dart
... ... @@ -73,8 +73,9 @@ class _StarRewardWidgetState extends State&lt;StarRewardWidget&gt;
73 73 setState(() {
74 74 _isVisible = false;
75 75 });
76   - widget.onAnimationEnd(); // 调用外部回调函数
77 76 }
  77 + Log.d('$StarRewardWidget whenCompleteOrCancel');
  78 + widget.onAnimationEnd(); // 调用外部回调函数
78 79 });
79 80 });
80 81 }
... ...
lib/pages/practice/bloc/topic_picture_bloc.dart
... ... @@ -7,7 +7,6 @@ import &#39;package:flutter/services.dart&#39;;
7 7 import 'package:flutter_bloc/flutter_bloc.dart';
8 8 import 'package:flutter_easyloading/flutter_easyloading.dart';
9 9 import 'package:permission_handler/permission_handler.dart';
10   -import 'package:wow_english/common/extension/string_extension.dart';
11 10 import 'package:wow_english/common/request/dao/listen_dao.dart';
12 11 import 'package:wow_english/common/request/exception.dart';
13 12 import 'package:wow_english/models/course_process_entity.dart';
... ... @@ -207,10 +206,14 @@ class TopicPictureBloc
207 206 if (checkAnswerRight(_optionSelectItem) == true) {
208 207 /// 如果选择题答(选)对后题目没播完,则暂停播放题目。答错的话继续播放体验也不错
209 208 await closePlayerResource();
210   - showStarRewardDialog(context);
211   - await _playResultSound(true);
  209 +
  210 + /// right音频长度比动效短,所以等动效完了再翻页
  211 + AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.right);
  212 + await showStarRewardDialog(context, onDismiss: () {
  213 + autoFlipPage();
  214 + });
212 215 } else {
213   - await _playResultSound(false);
  216 + await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.wrong);
214 217 }
215 218 }
216 219  
... ... @@ -276,14 +279,18 @@ class TopicPictureBloc
276 279 int score = int.parse(overall);
277 280 final voiceResult = VoiceResultType.fromScore(score);
278 281 if (voiceResult.lottieFilePath != null) {
279   - showCheerRewardDialog(context, lottieFile: voiceResult.lottieFilePath!);
  282 + AudioPlayerUtil.getInstance().playAudio(voiceResult.audioType);
  283 + await showCheerRewardDialog(context, lottieFile: voiceResult.lottieFilePath!, onDismiss: () {
  284 + if (isLastPage()) {
  285 + showStepPage();
  286 + }
  287 + });
  288 + } else {
  289 + await AudioPlayerUtil.getInstance().playAudio(voiceResult.audioType);
  290 + if (isLastPage()) {
  291 + showStepPage();
  292 + }
280 293 }
281   - await ClickWithMusicController.instance.playMusicAndPerformAction(
282   - context,
283   - voiceResult.audioType,
284   - () {
285   - if (isLastPage()) {showStepPage();};
286   - });
287 294 }
288 295  
289 296 // 暂时没用上
... ... @@ -319,29 +326,24 @@ class TopicPictureBloc
319 326 await ClickWithMusicController.instance.reset();
320 327 }
321 328  
322   - ///播放选择结果音效
323   - Future<void> _playResultSound(bool isCorrect) async {
324   - await ClickWithMusicController.instance.playMusicAndPerformAction(context,
325   - isCorrect ? AudioPlayerUtilType.right : AudioPlayerUtilType.wrong, () {
326   - if (isCorrect) {
327   - if (isLastPage()) {
328   - showStepPage();
329   - } else {
330   - // 答对后且播放完自动翻页
331   - pageController.nextPage(
332   - duration: const Duration(milliseconds: 250),
333   - curve: Curves.ease,
334   - );
335   - }
336   - }
337   - });
338   - }
339   -
340 329 ///是否是最后一页
341 330 bool isLastPage() {
342 331 return currentPage == _entity?.topics?.length;
343 332 }
344 333  
  334 + ///自动翻页
  335 + void autoFlipPage() {
  336 + if (isLastPage()) {
  337 + showStepPage();
  338 + } else {
  339 + // 答对后且播放完自动翻页
  340 + pageController.nextPage(
  341 + duration: const Duration(milliseconds: 250),
  342 + curve: Curves.ease,
  343 + );
  344 + }
  345 + }
  346 +
345 347 ///展示过渡页
346 348 void showStepPage() {
347 349 ///如果最后一页是语音问答题,评测完后自动翻页
... ...
lib/pages/reading/bloc/reading_bloc.dart
... ... @@ -22,6 +22,7 @@ import &#39;../../../models/course_process_entity.dart&#39;;
22 22 import '../../../models/singsound_result_detail_entity.dart';
23 23 import '../../../models/voice_result_type.dart';
24 24 import '../../../route/route.dart';
  25 +import '../../../utils/audio_player_util.dart';
25 26 import '../../../utils/loading.dart';
26 27  
27 28 import '../../../utils/log_util.dart';
... ... @@ -418,27 +419,35 @@ class ReadingPageBloc
418 419 add(OnXSVoiceStateChangeEvent());
419 420  
420 421 final voiceResult = VoiceResultType.fromScore(score);
  422 +
421 423 if (voiceResult.lottieFilePath != null) {
422   - showCheerRewardDialog(context, lottieFile: voiceResult.lottieFilePath!);
  424 + AudioPlayerUtil.getInstance().playAudio(voiceResult.audioType);
  425 + await showCheerRewardDialog(context, lottieFile: voiceResult.lottieFilePath!, onDismiss: () async {
  426 + await actionAfterRecord();
  427 + });
  428 + } else {
  429 + await AudioPlayerUtil.getInstance().playAudio(voiceResult.audioType);
  430 + await actionAfterRecord();
423 431 }
424   - await ClickWithMusicController.instance
425   - .playMusicAndPerformAction(context, voiceResult.audioType, () async {
426   - ///完成录音后紧接着播放录音
427   - await _playRecordAudioInner();
428   - if (isLastPage()) {
429   - sectionComplete(() {
430   - popPage(data: {
431   - 'currentStep': currentPage,
432   - 'courseLessonId': courseLessonId,
433   - 'isCompleted': true,
434   - 'nextSection': true
435   - });
436   - }, againSectionTap: () {
437   - _resetLocalResult();
438   - pageController.jumpToPage(0);
  432 + }
  433 +
  434 + /// 作答音效&动效播放结束后
  435 + Future<void> actionAfterRecord() async {
  436 + ///完成录音后紧接着播放录音
  437 + await _playRecordAudioInner();
  438 + if (isLastPage()) {
  439 + sectionComplete(() {
  440 + popPage(data: {
  441 + 'currentStep': currentPage,
  442 + 'courseLessonId': courseLessonId,
  443 + 'isCompleted': true,
  444 + 'nextSection': true
439 445 });
440   - }
441   - });
  446 + }, againSectionTap: () {
  447 + _resetLocalResult();
  448 + pageController.jumpToPage(0);
  449 + });
  450 + }
442 451 }
443 452  
444 453 ///终止评测
... ...