Commit cd32eb01c2cc308d80db020aa05f8860259410cb

Authored by 吴启风
1 parent 6baa39bd

feat:播放音乐防抖处理(课程环节页)

lib/common/utils/action_with_music_controller.dart 0 → 100644
  1 +import 'package:flutter/material.dart';
  2 +
  3 +import '../../utils/audio_player_util.dart';
  4 +
  5 +/// action前播放音乐控制类,维护状态做防抖处理
  6 +/// todo 需要结合生命周期,尤其是在声明周期结束后及时中断,避免action泄漏
  7 +class ActionWithMusicController {
  8 + ActionWithMusicController._privateConstructor();
  9 +
  10 + static final ActionWithMusicController _instance = ActionWithMusicController._privateConstructor();
  11 +
  12 + factory ActionWithMusicController() {
  13 + return _instance;
  14 + }
  15 +
  16 + bool _isPlaying = false;
  17 +
  18 + Future<void> playMusicAndPerformAction(BuildContext context,
  19 + AudioPlayerUtilType audioType, Function action) async {
  20 + if (_isPlaying) return;
  21 +
  22 + _isPlaying = true;
  23 +
  24 + // Play the music
  25 + await AudioPlayerUtil.getInstance()
  26 + .playAudio(audioType);
  27 +
  28 + action();
  29 +
  30 + _isPlaying = false;
  31 + }
  32 +
  33 + // void dispose() {
  34 + // _audioPlayer.dispose();
  35 + // }
  36 +}
0 \ No newline at end of file 37 \ No newline at end of file
lib/pages/section/bloc/section_bloc.dart
@@ -13,7 +13,6 @@ import &#39;package:wow_english/utils/toast_util.dart&#39;; @@ -13,7 +13,6 @@ import &#39;package:wow_english/utils/toast_util.dart&#39;;
13 import '../../../models/course_section_entity.dart'; 13 import '../../../models/course_section_entity.dart';
14 import '../../../models/course_unit_entity.dart'; 14 import '../../../models/course_unit_entity.dart';
15 import '../../../utils/list_ext.dart'; 15 import '../../../utils/list_ext.dart';
16 -import '../../../utils/log_util.dart';  
17 16
18 part 'section_event.dart'; 17 part 'section_event.dart';
19 18
@@ -50,15 +49,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -50,15 +49,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
50 Map<int, List<CourseSectionEntity>?> get courseSectionDatasMap => 49 Map<int, List<CourseSectionEntity>?> get courseSectionDatasMap =>
51 _courseSectionDatasMap; 50 _courseSectionDatasMap;
52 51
53 - ///点击环节后先请求数据再进入,标志位避免频繁点击多次请求(以及多次进入)  
54 - bool _isRequesting = false;  
55 -  
56 SectionBloc(this._courseUnitEntity, this._currentPage, this._pageController, 52 SectionBloc(this._courseUnitEntity, this._currentPage, this._pageController,
57 this._listController, this._indicatorSrollController) 53 this._listController, this._indicatorSrollController)
58 : super(LessonInitial()) { 54 : super(LessonInitial()) {
59 on<RequestDataEvent>(_requestSectionsData); 55 on<RequestDataEvent>(_requestSectionsData);
60 on<RequestEndClassEvent>(_requestEndClass); 56 on<RequestEndClassEvent>(_requestEndClass);
61 - on<RequestEnterClassEvent>(_requestEnterClass); 57 + on<RequestEnterClassEvent>(_onEnterClass);
62 on<CurrentUnitIndexChangeEvent>(_pageControllerChange); 58 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
63 on<InitEvent>((event, emit) async { 59 on<InitEvent>((event, emit) async {
64 await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe); 60 await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe);
@@ -84,25 +80,30 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; { @@ -84,25 +80,30 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
84 } 80 }
85 } 81 }
86 82
87 - void _requestEnterClass( 83 + void _onEnterClass(
88 RequestEnterClassEvent event, Emitter<SectionState> emitter) async { 84 RequestEnterClassEvent event, Emitter<SectionState> emitter) async {
89 - try {  
90 - Log.d("WQF _requestVideoLesson _isRequesting=$_isRequesting");  
91 - if (_isRequesting) {  
92 - return;  
93 - }  
94 - _isRequesting = true;  
95 - await loading(() async {  
96 - await ListenDao.enterClass(event.courseLessonId);  
97 - emitter(RequestEnterClassState(event.courseLessonId, event.courseType));  
98 - });  
99 - } catch (e) {  
100 - if (e is ApiException) {  
101 - showToast(e.message ?? '请求失败,请检查网络连接'); 85 + emitter(RequestEnterClassState(event.courseLessonId, event.courseType));
  86 + }
  87 +
  88 + ///进入课程接口请求函数封装
  89 + ///onRequestEnterSuccess 接口请求成功后行为函数,比如跳转课程页面
  90 + Future<void> requestEnterClass(String courseLessonId, Function onRequestEnterSuccess,
  91 + {Function? onRequestEnterFailed}) async {
  92 + await loading(() async {
  93 + try {
  94 + await ListenDao.enterClass(courseLessonId);
  95 + onRequestEnterSuccess();
  96 + } catch (e) {
  97 + if (e is ApiException) {
  98 + showToast(e.message ?? '请求失败,请检查网络连接');
  99 + } else {
  100 + showToast('$e');
  101 + }
  102 + if (onRequestEnterFailed != null) {
  103 + onRequestEnterFailed(e);
  104 + }
102 } 105 }
103 - } finally {  
104 - _isRequesting = false;  
105 - } 106 + });
106 } 107 }
107 108
108 void _requestEndClass( 109 void _requestEndClass(
lib/pages/section/section_page.dart
1 -import 'package:audioplayers/audioplayers.dart';  
2 import 'package:flutter/cupertino.dart'; 1 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart'; 2 import 'package:flutter/material.dart';
4 import 'package:flutter_bloc/flutter_bloc.dart'; 3 import 'package:flutter_bloc/flutter_bloc.dart';
5 import 'package:flutter_screenutil/flutter_screenutil.dart'; 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
6 import 'package:nested_scroll_views/material.dart'; 5 import 'package:nested_scroll_views/material.dart';
7 import 'package:wow_english/common/core/user_util.dart'; 6 import 'package:wow_english/common/core/user_util.dart';
8 -import 'package:wow_english/common/extension/string_extension.dart'; 7 +import 'package:wow_english/common/utils/action_with_music_controller.dart';
9 import 'package:wow_english/models/course_unit_entity.dart'; 8 import 'package:wow_english/models/course_unit_entity.dart';
10 import 'package:wow_english/pages/section/section_type.dart'; 9 import 'package:wow_english/pages/section/section_type.dart';
11 import 'package:wow_english/pages/section/widgets/section_item.dart'; 10 import 'package:wow_english/pages/section/widgets/section_item.dart';
@@ -13,7 +12,6 @@ import &#39;package:wow_english/pages/section/widgets/section_bouns_item.dart&#39;; @@ -13,7 +12,6 @@ import &#39;package:wow_english/pages/section/widgets/section_bouns_item.dart&#39;;
13 import 'package:wow_english/pages/section/widgets/section_header_widget.dart'; 12 import 'package:wow_english/pages/section/widgets/section_header_widget.dart';
14 import 'package:wow_english/route/route.dart'; 13 import 'package:wow_english/route/route.dart';
15 import 'package:wow_english/utils/audio_player_util.dart'; 14 import 'package:wow_english/utils/audio_player_util.dart';
16 -import 'package:wow_english/utils/log_util.dart';  
17 import 'package:wow_english/utils/toast_util.dart'; 15 import 'package:wow_english/utils/toast_util.dart';
18 16
19 import '../../models/course_section_entity.dart'; 17 import '../../models/course_section_entity.dart';
@@ -58,6 +56,7 @@ class _SectionPageView extends StatelessWidget { @@ -58,6 +56,7 @@ class _SectionPageView extends StatelessWidget {
58 @override 56 @override
59 Widget build(BuildContext context) { 57 Widget build(BuildContext context) {
60 final bloc = BlocProvider.of<SectionBloc>(context); 58 final bloc = BlocProvider.of<SectionBloc>(context);
  59 + final controller = ActionWithMusicController();
61 return BlocListener<SectionBloc, SectionState>( 60 return BlocListener<SectionBloc, SectionState>(
62 listener: (context, state) async { 61 listener: (context, state) async {
63 if (state is RequestEnterClassState) { 62 if (state is RequestEnterClassState) {
@@ -66,72 +65,79 @@ class _SectionPageView extends StatelessWidget { @@ -66,72 +65,79 @@ class _SectionPageView extends StatelessWidget {
66 var title = 65 var title =
67 state.courseType == SectionType.song.value ? 'song' : 'video'; 66 state.courseType == SectionType.song.value ? 'song' : 'video';
68 67
  68 + AudioPlayerUtilType audioType;
69 ///儿歌/视频类型 69 ///儿歌/视频类型
70 if (state.courseType == SectionType.song.value) { 70 if (state.courseType == SectionType.song.value) {
71 - await AudioPlayerUtil.getInstance()  
72 - .playAudio(AudioPlayerUtilType.musicTime); 71 + audioType = AudioPlayerUtilType.musicTime;
73 } else { 72 } else {
74 - await AudioPlayerUtil.getInstance()  
75 - .playAudio(AudioPlayerUtilType.videoTime); 73 + audioType = AudioPlayerUtilType.videoTime;
76 } 74 }
77 - pushNamed(AppRouteName.lookVideo, arguments: {  
78 - 'videoUrl': null,  
79 - 'title': title,  
80 - 'courseLessonId': state.courseLessonId,  
81 - 'isTopic': true  
82 - }).then((value) {  
83 - if (value != null) {  
84 - Map<String, dynamic> dataMap = value as Map<String, dynamic>;  
85 - bloc.add(RequestEndClassEvent(  
86 - dataMap['courseLessonId']!, dataMap['isCompleted'],  
87 - currentTime: dataMap['currentTime'],  
88 - autoNextSection: dataMap['nextSection']));  
89 - }  
90 - AudioPlayerUtil.getInstance()  
91 - .playAudio(AudioPlayerUtilType.countWithMe); 75 +
  76 + controller.playMusicAndPerformAction(context, audioType, () async {
  77 + ///播放音乐->调进入课程接口->跳转课程页面
  78 + bloc.requestEnterClass(state.courseLessonId, () {
  79 + pushNamed(AppRouteName.lookVideo, arguments: {
  80 + 'videoUrl': null,
  81 + 'title': title,
  82 + 'courseLessonId': state.courseLessonId,
  83 + 'isTopic': true
  84 + }).then((value) {
  85 + if (value != null) {
  86 + Map<String, dynamic> dataMap =
  87 + value as Map<String, dynamic>;
  88 + bloc.add(RequestEndClassEvent(
  89 + dataMap['courseLessonId']!, dataMap['isCompleted'],
  90 + currentTime: dataMap['currentTime'],
  91 + autoNextSection: dataMap['nextSection']));
  92 + }
  93 + AudioPlayerUtil.getInstance()
  94 + .playAudio(AudioPlayerUtilType.countWithMe);
  95 + });
  96 + });
92 }); 97 });
93 return; 98 return;
94 } 99 }
95 100
96 if (state.courseType == SectionType.pictureBook.value) { 101 if (state.courseType == SectionType.pictureBook.value) {
97 - await AudioPlayerUtil.getInstance()  
98 - .playAudio(AudioPlayerUtilType.readingTime);  
99 //绘本 102 //绘本
100 - pushNamed(AppRouteName.reading,  
101 - arguments: {'courseLessonId': state.courseLessonId})  
102 - .then((value) {  
103 - if (value != null) {  
104 - Map<String, dynamic> dataMap = value as Map<String, dynamic>;  
105 - bloc.add(RequestEndClassEvent(  
106 - dataMap['courseLessonId']!,  
107 - dataMap['isCompleted'],  
108 - currentStep: dataMap['currentStep'],  
109 - autoNextSection: dataMap['nextSection'],  
110 - ));  
111 - AudioPlayerUtil.getInstance()  
112 - .playAudio(AudioPlayerUtilType.countWithMe);  
113 - } 103 + controller.playMusicAndPerformAction(
  104 + context, AudioPlayerUtilType.readingTime, () async {
  105 + pushNamed(AppRouteName.reading,
  106 + arguments: {'courseLessonId': state.courseLessonId})
  107 + .then((value) {
  108 + if (value != null) {
  109 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  110 + bloc.add(RequestEndClassEvent(
  111 + dataMap['courseLessonId']!,
  112 + dataMap['isCompleted'],
  113 + currentStep: dataMap['currentStep'],
  114 + autoNextSection: dataMap['nextSection'],
  115 + ));
  116 + AudioPlayerUtil.getInstance()
  117 + .playAudio(AudioPlayerUtilType.countWithMe);
  118 + }
  119 + });
114 }); 120 });
115 -  
116 return; 121 return;
117 } 122 }
118 123
119 if (state.courseType == SectionType.practice.value) { 124 if (state.courseType == SectionType.practice.value) {
120 //练习 125 //练习
121 - await AudioPlayerUtil.getInstance()  
122 - .playAudio(AudioPlayerUtilType.quizTime);  
123 - pushNamed(AppRouteName.topicPic,  
124 - arguments: {'courseLessonId': state.courseLessonId})  
125 - .then((value) {  
126 - if (value != null) {  
127 - Map<String, dynamic> dataMap = value as Map<String, dynamic>;  
128 - bloc.add(RequestEndClassEvent(  
129 - dataMap['courseLessonId']!, dataMap['isCompleted'],  
130 - currentStep: dataMap['currentStep'],  
131 - autoNextSection: dataMap['nextSection']));  
132 - }  
133 - AudioPlayerUtil.getInstance()  
134 - .playAudio(AudioPlayerUtilType.countWithMe); 126 + controller.playMusicAndPerformAction(
  127 + context, AudioPlayerUtilType.quizTime, () async {
  128 + pushNamed(AppRouteName.topicPic,
  129 + arguments: {'courseLessonId': state.courseLessonId})
  130 + .then((value) {
  131 + if (value != null) {
  132 + Map<String, dynamic> dataMap = value as Map<String, dynamic>;
  133 + bloc.add(RequestEndClassEvent(
  134 + dataMap['courseLessonId']!, dataMap['isCompleted'],
  135 + currentStep: dataMap['currentStep'],
  136 + autoNextSection: dataMap['nextSection']));
  137 + }
  138 + AudioPlayerUtil.getInstance()
  139 + .playAudio(AudioPlayerUtilType.countWithMe);
  140 + });
135 }); 141 });
136 return; 142 return;
137 } 143 }