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 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 13 import '../../../models/course_section_entity.dart';
14 14 import '../../../models/course_unit_entity.dart';
15 15 import '../../../utils/list_ext.dart';
16   -import '../../../utils/log_util.dart';
17 16  
18 17 part 'section_event.dart';
19 18  
... ... @@ -50,15 +49,12 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
50 49 Map<int, List<CourseSectionEntity>?> get courseSectionDatasMap =>
51 50 _courseSectionDatasMap;
52 51  
53   - ///点击环节后先请求数据再进入,标志位避免频繁点击多次请求(以及多次进入)
54   - bool _isRequesting = false;
55   -
56 52 SectionBloc(this._courseUnitEntity, this._currentPage, this._pageController,
57 53 this._listController, this._indicatorSrollController)
58 54 : super(LessonInitial()) {
59 55 on<RequestDataEvent>(_requestSectionsData);
60 56 on<RequestEndClassEvent>(_requestEndClass);
61   - on<RequestEnterClassEvent>(_requestEnterClass);
  57 + on<RequestEnterClassEvent>(_onEnterClass);
62 58 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
63 59 on<InitEvent>((event, emit) async {
64 60 await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe);
... ... @@ -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 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 109 void _requestEndClass(
... ...
lib/pages/section/section_page.dart
1   -import 'package:audioplayers/audioplayers.dart';
2 1 import 'package:flutter/cupertino.dart';
3 2 import 'package:flutter/material.dart';
4 3 import 'package:flutter_bloc/flutter_bloc.dart';
5 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
6 5 import 'package:nested_scroll_views/material.dart';
7 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 8 import 'package:wow_english/models/course_unit_entity.dart';
10 9 import 'package:wow_english/pages/section/section_type.dart';
11 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 12 import 'package:wow_english/pages/section/widgets/section_header_widget.dart';
14 13 import 'package:wow_english/route/route.dart';
15 14 import 'package:wow_english/utils/audio_player_util.dart';
16   -import 'package:wow_english/utils/log_util.dart';
17 15 import 'package:wow_english/utils/toast_util.dart';
18 16  
19 17 import '../../models/course_section_entity.dart';
... ... @@ -58,6 +56,7 @@ class _SectionPageView extends StatelessWidget {
58 56 @override
59 57 Widget build(BuildContext context) {
60 58 final bloc = BlocProvider.of<SectionBloc>(context);
  59 + final controller = ActionWithMusicController();
61 60 return BlocListener<SectionBloc, SectionState>(
62 61 listener: (context, state) async {
63 62 if (state is RequestEnterClassState) {
... ... @@ -66,72 +65,79 @@ class _SectionPageView extends StatelessWidget {
66 65 var title =
67 66 state.courseType == SectionType.song.value ? 'song' : 'video';
68 67  
  68 + AudioPlayerUtilType audioType;
69 69 ///儿歌/视频类型
70 70 if (state.courseType == SectionType.song.value) {
71   - await AudioPlayerUtil.getInstance()
72   - .playAudio(AudioPlayerUtilType.musicTime);
  71 + audioType = AudioPlayerUtilType.musicTime;
73 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 98 return;
94 99 }
95 100  
96 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 121 return;
117 122 }
118 123  
119 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 142 return;
137 143 }
... ...