Commit 0493c1044f4ffedea1543ff82814cd3f8f08d9b1

Authored by 吴启风
1 parent cd32eb01

feat:带音乐点击防抖函数优化

lib/common/utils/action_with_music_controller.dart renamed to lib/common/utils/click_with_music_controller.dart
  1 +import 'dart:async';
  2 +
1 3 import 'package:flutter/material.dart';
2 4  
3 5 import '../../utils/audio_player_util.dart';
  6 +import '../../utils/log_util.dart';
4 7  
5 8 /// action前播放音乐控制类,维护状态做防抖处理
6 9 /// todo 需要结合生命周期,尤其是在声明周期结束后及时中断,避免action泄漏
7   -class ActionWithMusicController {
8   - ActionWithMusicController._privateConstructor();
  10 +class ClickWithMusicController {
9 11  
10   - static final ActionWithMusicController _instance = ActionWithMusicController._privateConstructor();
  12 + static ClickWithMusicController? _instance;
11 13  
12   - factory ActionWithMusicController() {
13   - return _instance;
14   - }
  14 + ClickWithMusicController._privateConstructor();
  15 +
  16 + static ClickWithMusicController get instance => _instance ??= ClickWithMusicController._privateConstructor();
15 17  
16 18 bool _isPlaying = false;
17 19  
  20 + ///@param action 可以是同步函数也可以是异步函数
18 21 Future<void> playMusicAndPerformAction(BuildContext context,
19   - AudioPlayerUtilType audioType, Function action) async {
  22 + AudioPlayerUtilType audioType, FutureOr<void> Function() action) async {
20 23 if (_isPlaying) return;
21 24  
22 25 _isPlaying = true;
  26 + Log.d("WQF playMusicAndPerformAction playAudio begin");
23 27  
24 28 // Play the music
25 29 await AudioPlayerUtil.getInstance()
26 30 .playAudio(audioType);
27 31  
28   - action();
29   -
30   - _isPlaying = false;
  32 + try {
  33 + await Future.sync(action);
  34 + } catch (e) {
  35 + Log.d('WQF playMusicAndPerformAction exception $e');
  36 + } finally {
  37 + Log.d("WQF playMusicAndPerformAction playAudio end");
  38 + _isPlaying = false;
  39 + }
31 40 }
32 41  
33 42 // void dispose() {
... ...
lib/pages/section/bloc/section_bloc.dart
  1 +import 'dart:async';
  2 +
1 3 import 'package:flutter/cupertino.dart';
2 4 import 'package:flutter/foundation.dart';
3 5 import 'package:flutter/material.dart';
... ... @@ -57,7 +59,8 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
57 59 on<RequestEnterClassEvent>(_onEnterClass);
58 60 on<CurrentUnitIndexChangeEvent>(_pageControllerChange);
59 61 on<InitEvent>((event, emit) async {
60   - await AudioPlayerUtil.getInstance().playAudio(AudioPlayerUtilType.countWithMe);
  62 + await AudioPlayerUtil.getInstance()
  63 + .playAudio(AudioPlayerUtilType.countWithMe);
61 64 });
62 65 }
63 66  
... ... @@ -87,20 +90,21 @@ class SectionBloc extends Bloc&lt;SectionEvent, SectionState&gt; {
87 90  
88 91 ///进入课程接口请求函数封装
89 92 ///onRequestEnterSuccess 接口请求成功后行为函数,比如跳转课程页面
90   - Future<void> requestEnterClass(String courseLessonId, Function onRequestEnterSuccess,
91   - {Function? onRequestEnterFailed}) async {
  93 + Future<void> requestEnterClass(
  94 + String courseLessonId, FutureOr<void> Function() onRequestEnterSuccess,
  95 + {FutureOr<void> Function(Object)? onRequestEnterFailed}) async {
92 96 await loading(() async {
93 97 try {
94 98 await ListenDao.enterClass(courseLessonId);
95   - onRequestEnterSuccess();
  99 + await Future.sync(onRequestEnterSuccess);
96 100 } catch (e) {
97 101 if (e is ApiException) {
98   - showToast(e.message ?? '请求失败,请检查网络连接');
  102 + showToast('进入课堂失败 ${e.message}');
99 103 } else {
100   - showToast('$e');
  104 + showToast('进入课堂失败 $e');
101 105 }
102 106 if (onRequestEnterFailed != null) {
103   - onRequestEnterFailed(e);
  107 + await Future.sync(onRequestEnterFailed(e) as FutureOr Function());
104 108 }
105 109 }
106 110 });
... ...
lib/pages/section/section_page.dart
... ... @@ -4,7 +4,7 @@ import &#39;package:flutter_bloc/flutter_bloc.dart&#39;;
4 4 import 'package:flutter_screenutil/flutter_screenutil.dart';
5 5 import 'package:nested_scroll_views/material.dart';
6 6 import 'package:wow_english/common/core/user_util.dart';
7   -import 'package:wow_english/common/utils/action_with_music_controller.dart';
  7 +import 'package:wow_english/common/utils/click_with_music_controller.dart';
8 8 import 'package:wow_english/models/course_unit_entity.dart';
9 9 import 'package:wow_english/pages/section/section_type.dart';
10 10 import 'package:wow_english/pages/section/widgets/section_item.dart';
... ... @@ -15,6 +15,7 @@ import &#39;package:wow_english/utils/audio_player_util.dart&#39;;
15 15 import 'package:wow_english/utils/toast_util.dart';
16 16  
17 17 import '../../models/course_section_entity.dart';
  18 +import '../../utils/log_util.dart';
18 19 import 'bloc/section_bloc.dart';
19 20 import 'courese_module_model.dart';
20 21  
... ... @@ -56,30 +57,34 @@ class _SectionPageView extends StatelessWidget {
56 57 @override
57 58 Widget build(BuildContext context) {
58 59 final bloc = BlocProvider.of<SectionBloc>(context);
59   - final controller = ActionWithMusicController();
  60 + final clickController = ClickWithMusicController.instance;
60 61 return BlocListener<SectionBloc, SectionState>(
61 62 listener: (context, state) async {
62 63 if (state is RequestEnterClassState) {
63   - if (state.courseType == SectionType.song.value ||
64   - state.courseType == SectionType.video.value) {
65   - var title =
66   - state.courseType == SectionType.song.value ? 'song' : 'video';
  64 + final courseType = state.courseType;
  65 + final courseLessonId = state.courseLessonId;
  66 + if (courseType == SectionType.song.value ||
  67 + courseType == SectionType.video.value) {
  68 + final title =
  69 + courseType == SectionType.song.value ? 'song' : 'video';
  70 + final AudioPlayerUtilType audioType;
67 71  
68   - AudioPlayerUtilType audioType;
69 72 ///儿歌/视频类型
70   - if (state.courseType == SectionType.song.value) {
  73 + if (courseType == SectionType.song.value) {
71 74 audioType = AudioPlayerUtilType.musicTime;
72 75 } else {
73 76 audioType = AudioPlayerUtilType.videoTime;
74 77 }
75 78  
76   - controller.playMusicAndPerformAction(context, audioType, () async {
  79 + clickController.playMusicAndPerformAction(context, audioType,
  80 + () async {
77 81 ///播放音乐->调进入课程接口->跳转课程页面
78   - bloc.requestEnterClass(state.courseLessonId, () {
  82 + await bloc.requestEnterClass(courseLessonId, () {
  83 + Log.d("WQF request finish");
79 84 pushNamed(AppRouteName.lookVideo, arguments: {
80 85 'videoUrl': null,
81 86 'title': title,
82   - 'courseLessonId': state.courseLessonId,
  87 + 'courseLessonId': courseLessonId,
83 88 'isTopic': true
84 89 }).then((value) {
85 90 if (value != null) {
... ... @@ -93,50 +98,58 @@ class _SectionPageView extends StatelessWidget {
93 98 AudioPlayerUtil.getInstance()
94 99 .playAudio(AudioPlayerUtilType.countWithMe);
95 100 });
  101 + }, onRequestEnterFailed: (error) {
  102 + Log.d("WQF requestEnterClass failed $error");
96 103 });
97 104 });
98 105 return;
99 106 }
100 107  
101   - if (state.courseType == SectionType.pictureBook.value) {
  108 + if (courseType == SectionType.pictureBook.value) {
102 109 //绘本
103   - controller.playMusicAndPerformAction(
  110 + clickController.playMusicAndPerformAction(
104 111 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   - }
  112 + await bloc.requestEnterClass(courseLessonId, () {
  113 + pushNamed(AppRouteName.reading,
  114 + arguments: {'courseLessonId': courseLessonId})
  115 + .then((value) {
  116 + if (value != null) {
  117 + Map<String, dynamic> dataMap =
  118 + value as Map<String, dynamic>;
  119 + bloc.add(RequestEndClassEvent(
  120 + dataMap['courseLessonId']!,
  121 + dataMap['isCompleted'],
  122 + currentStep: dataMap['currentStep'],
  123 + autoNextSection: dataMap['nextSection'],
  124 + ));
  125 + AudioPlayerUtil.getInstance()
  126 + .playAudio(AudioPlayerUtilType.countWithMe);
  127 + }
  128 + });
119 129 });
120 130 });
121 131 return;
122 132 }
123 133  
124   - if (state.courseType == SectionType.practice.value) {
  134 + if (courseType == SectionType.practice.value) {
125 135 //练习
126   - controller.playMusicAndPerformAction(
  136 + clickController.playMusicAndPerformAction(
127 137 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);
  138 + await bloc.requestEnterClass(courseLessonId, () {
  139 + pushNamed(AppRouteName.topicPic,
  140 + arguments: {'courseLessonId': courseLessonId})
  141 + .then((value) {
  142 + if (value != null) {
  143 + Map<String, dynamic> dataMap =
  144 + value as Map<String, dynamic>;
  145 + bloc.add(RequestEndClassEvent(
  146 + dataMap['courseLessonId']!, dataMap['isCompleted'],
  147 + currentStep: dataMap['currentStep'],
  148 + autoNextSection: dataMap['nextSection']));
  149 + }
  150 + AudioPlayerUtil.getInstance()
  151 + .playAudio(AudioPlayerUtilType.countWithMe);
  152 + });
140 153 });
141 154 });
142 155 return;
... ...