Commit 523864dd18878775b430d258c0b8a47db684dc46
1 parent
b2af9c1c
feat:音频播放单例增加声明周期感知
Showing
3 changed files
with
73 additions
and
55 deletions
lib/pages/games/bloc.dart
... | ... | @@ -44,7 +44,7 @@ class GamesBloc extends Bloc<GamesEvent, GamesState> { |
44 | 44 | } |
45 | 45 | |
46 | 46 | void _gotoGamePage(GotoGamePageEvent event, Emitter<GamesState> emit) async { |
47 | - AudioPlayerUtil.getInstance().pause(); | |
47 | + await AudioPlayerUtil.getInstance().pause(); | |
48 | 48 | try { |
49 | 49 | _methodChannel = const MethodChannel('wow_english/game_method_channel'); |
50 | 50 | await _methodChannel | ... | ... |
lib/pages/section/section_page.dart
... | ... | @@ -59,7 +59,7 @@ class _SectionPageView extends StatelessWidget { |
59 | 59 | Widget build(BuildContext context) { |
60 | 60 | final bloc = BlocProvider.of<SectionBloc>(context); |
61 | 61 | return BlocListener<SectionBloc, SectionState>( |
62 | - listener: (context, state) { | |
62 | + listener: (context, state) async { | |
63 | 63 | if (state is RequestVideoLessonState) { |
64 | 64 | final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; |
65 | 65 | var title = ''; |
... | ... | @@ -103,40 +103,36 @@ class _SectionPageView extends StatelessWidget { |
103 | 103 | ///视频类型 |
104 | 104 | ///获取视频课程内容 |
105 | 105 | if (state.courseType == 1) { |
106 | - AudioPlayerUtil.getInstance() | |
106 | + await AudioPlayerUtil.getInstance() | |
107 | 107 | .playAudio(AudioPlayerUtilType.musicTime); |
108 | 108 | } else { |
109 | - AudioPlayerUtil.getInstance() | |
109 | + await AudioPlayerUtil.getInstance() | |
110 | 110 | .playAudio(AudioPlayerUtilType.videoTime); |
111 | 111 | } |
112 | - Future.delayed(const Duration(seconds: 1), () { | |
113 | - bloc.add(RequestVideoLessonEvent( | |
114 | - state.courseLessonId, state.courseType)); | |
115 | - }); | |
112 | + bloc.add(RequestVideoLessonEvent( | |
113 | + state.courseLessonId, state.courseType)); | |
116 | 114 | |
117 | 115 | return; |
118 | 116 | } |
119 | 117 | |
120 | 118 | if (state.courseType == SectionType.pictureBook.value) { |
121 | - AudioPlayerUtil.getInstance() | |
119 | + await AudioPlayerUtil.getInstance() | |
122 | 120 | .playAudio(AudioPlayerUtilType.readingTime); |
123 | - Future.delayed(const Duration(seconds: 1), () { | |
124 | - //绘本 | |
125 | - pushNamed(AppRouteName.reading, | |
126 | - arguments: {'courseLessonId': state.courseLessonId}) | |
127 | - .then((value) { | |
128 | - if (value != null) { | |
129 | - Map<String, dynamic> dataMap = value as Map<String, dynamic>; | |
130 | - bloc.add(RequestEndClassEvent( | |
131 | - dataMap['courseLessonId']!, | |
132 | - dataMap['isCompleted'], | |
133 | - currentStep: dataMap['currentStep'], | |
134 | - autoNextSection: dataMap['nextSection'], | |
135 | - )); | |
136 | - AudioPlayerUtil.getInstance() | |
137 | - .playAudio(AudioPlayerUtilType.countWithMe); | |
138 | - } | |
139 | - }); | |
121 | + //绘本 | |
122 | + pushNamed(AppRouteName.reading, | |
123 | + arguments: {'courseLessonId': state.courseLessonId}) | |
124 | + .then((value) { | |
125 | + if (value != null) { | |
126 | + Map<String, dynamic> dataMap = value as Map<String, dynamic>; | |
127 | + bloc.add(RequestEndClassEvent( | |
128 | + dataMap['courseLessonId']!, | |
129 | + dataMap['isCompleted'], | |
130 | + currentStep: dataMap['currentStep'], | |
131 | + autoNextSection: dataMap['nextSection'], | |
132 | + )); | |
133 | + AudioPlayerUtil.getInstance() | |
134 | + .playAudio(AudioPlayerUtilType.countWithMe); | |
135 | + } | |
140 | 136 | }); |
141 | 137 | |
142 | 138 | return; |
... | ... | @@ -144,22 +140,20 @@ class _SectionPageView extends StatelessWidget { |
144 | 140 | |
145 | 141 | if (state.courseType == SectionType.practice.value) { |
146 | 142 | //练习 |
147 | - AudioPlayerUtil.getInstance() | |
143 | + await AudioPlayerUtil.getInstance() | |
148 | 144 | .playAudio(AudioPlayerUtilType.quizTime); |
149 | - Future.delayed(const Duration(seconds: 1), () { | |
150 | - pushNamed(AppRouteName.topicPic, | |
151 | - arguments: {'courseLessonId': state.courseLessonId}) | |
152 | - .then((value) { | |
153 | - if (value != null) { | |
154 | - Map<String, dynamic> dataMap = value as Map<String, dynamic>; | |
155 | - bloc.add(RequestEndClassEvent( | |
156 | - dataMap['courseLessonId']!, dataMap['isCompleted'], | |
157 | - currentStep: dataMap['currentStep'], | |
158 | - autoNextSection: dataMap['nextSection'])); | |
159 | - } | |
160 | - AudioPlayerUtil.getInstance() | |
161 | - .playAudio(AudioPlayerUtilType.countWithMe); | |
162 | - }); | |
145 | + pushNamed(AppRouteName.topicPic, | |
146 | + arguments: {'courseLessonId': state.courseLessonId}) | |
147 | + .then((value) { | |
148 | + if (value != null) { | |
149 | + Map<String, dynamic> dataMap = value as Map<String, dynamic>; | |
150 | + bloc.add(RequestEndClassEvent( | |
151 | + dataMap['courseLessonId']!, dataMap['isCompleted'], | |
152 | + currentStep: dataMap['currentStep'], | |
153 | + autoNextSection: dataMap['nextSection'])); | |
154 | + } | |
155 | + AudioPlayerUtil.getInstance() | |
156 | + .playAudio(AudioPlayerUtilType.countWithMe); | |
163 | 157 | }); |
164 | 158 | return; |
165 | 159 | } | ... | ... |
lib/utils/audio_player_util.dart
1 | 1 | import 'package:audioplayers/audioplayers.dart'; |
2 | +import 'package:flutter/cupertino.dart'; | |
2 | 3 | import 'package:wow_english/common/extension/string_extension.dart'; |
3 | 4 | |
4 | 5 | enum AudioPlayerUtilType { |
... | ... | @@ -18,15 +19,18 @@ enum AudioPlayerUtilType { |
18 | 19 | final String path; |
19 | 20 | } |
20 | 21 | |
21 | -class AudioPlayerUtil { | |
22 | +class AudioPlayerUtil extends WidgetsBindingObserver { | |
22 | 23 | static AudioPlayerUtil? _instance; |
23 | - late AudioPlayer audioPlayer; | |
24 | + late AudioPlayer _audioPlayer; | |
24 | 25 | late AudioPlayerUtilType currentType; |
26 | + bool _wasPlaying = false; | |
25 | 27 | |
26 | 28 | // 私有构造函数 |
27 | 29 | AudioPlayerUtil._internal() { |
28 | - audioPlayer = AudioPlayer(); | |
29 | - audioPlayer.onPlayerStateChanged.listen((event) async { | |
30 | + // 监听应用生命周期 | |
31 | + WidgetsBinding.instance.addObserver(this); | |
32 | + _audioPlayer = AudioPlayer(); | |
33 | + _audioPlayer.onPlayerStateChanged.listen((event) async { | |
30 | 34 | if (event == PlayerState.completed) { |
31 | 35 | // 播放结束再次播放 |
32 | 36 | if (currentType == AudioPlayerUtilType.inMyTummy) { |
... | ... | @@ -56,26 +60,46 @@ class AudioPlayerUtil { |
56 | 60 | Future<void> playAudio(AudioPlayerUtilType type) async { |
57 | 61 | currentType = type; |
58 | 62 | String path = type.path; |
59 | - await audioPlayer.play(AssetSource(path.assetMp3), volume: 0.5); | |
60 | - await audioPlayer.onPlayerComplete.first; | |
63 | + await _audioPlayer.play(AssetSource(path.assetMp3), volume: 0.5); | |
64 | + await _audioPlayer.onPlayerComplete.first; | |
61 | 65 | } |
62 | 66 | |
63 | 67 | // stop |
64 | - void stop() { | |
65 | - audioPlayer.stop(); | |
68 | + Future<void> stop() async { | |
69 | + _audioPlayer.stop(); | |
66 | 70 | } |
67 | 71 | |
68 | 72 | // pause |
69 | - void pause() { | |
70 | - if (audioPlayer.state == PlayerState.playing) { | |
71 | - audioPlayer.pause(); | |
73 | + Future<void> pause() async { | |
74 | + if (_audioPlayer.state == PlayerState.playing) { | |
75 | + _audioPlayer.pause(); | |
72 | 76 | } |
73 | 77 | } |
74 | 78 | |
75 | 79 | // resume |
76 | - void resume() { | |
77 | - if (audioPlayer.state == PlayerState.paused) { | |
78 | - audioPlayer.resume(); | |
80 | + Future<void> resume() async { | |
81 | + if (_audioPlayer.state == PlayerState.paused) { | |
82 | + _audioPlayer.resume(); | |
79 | 83 | } |
80 | 84 | } |
85 | + | |
86 | + @override | |
87 | + void didChangeAppLifecycleState(AppLifecycleState state) async { | |
88 | + if (state == AppLifecycleState.paused) { | |
89 | + if (_audioPlayer.state == PlayerState.playing) { | |
90 | + _wasPlaying = true; | |
91 | + await pause(); | |
92 | + }; | |
93 | + } else if (state == AppLifecycleState.resumed) { | |
94 | + if (_wasPlaying == true) { | |
95 | + _wasPlaying = false; | |
96 | + await resume(); | |
97 | + } | |
98 | + } | |
99 | + } | |
100 | + | |
101 | + void dispose() { | |
102 | + _audioPlayer.dispose(); | |
103 | + WidgetsBinding.instance.removeObserver(this); | |
104 | + } | |
81 | 105 | } | ... | ... |