Commit 523864dd18878775b430d258c0b8a47db684dc46

Authored by 吴启风
1 parent b2af9c1c

feat:音频播放单例增加声明周期感知

lib/pages/games/bloc.dart
@@ -44,7 +44,7 @@ class GamesBloc extends Bloc<GamesEvent, GamesState> { @@ -44,7 +44,7 @@ class GamesBloc extends Bloc<GamesEvent, GamesState> {
44 } 44 }
45 45
46 void _gotoGamePage(GotoGamePageEvent event, Emitter<GamesState> emit) async { 46 void _gotoGamePage(GotoGamePageEvent event, Emitter<GamesState> emit) async {
47 - AudioPlayerUtil.getInstance().pause(); 47 + await AudioPlayerUtil.getInstance().pause();
48 try { 48 try {
49 _methodChannel = const MethodChannel('wow_english/game_method_channel'); 49 _methodChannel = const MethodChannel('wow_english/game_method_channel');
50 await _methodChannel 50 await _methodChannel
lib/pages/section/section_page.dart
@@ -59,7 +59,7 @@ class _SectionPageView extends StatelessWidget { @@ -59,7 +59,7 @@ class _SectionPageView extends StatelessWidget {
59 Widget build(BuildContext context) { 59 Widget build(BuildContext context) {
60 final bloc = BlocProvider.of<SectionBloc>(context); 60 final bloc = BlocProvider.of<SectionBloc>(context);
61 return BlocListener<SectionBloc, SectionState>( 61 return BlocListener<SectionBloc, SectionState>(
62 - listener: (context, state) { 62 + listener: (context, state) async {
63 if (state is RequestVideoLessonState) { 63 if (state is RequestVideoLessonState) {
64 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? ''; 64 final videoUrl = bloc.processEntity?.videos?.videoUrl ?? '';
65 var title = ''; 65 var title = '';
@@ -103,40 +103,36 @@ class _SectionPageView extends StatelessWidget { @@ -103,40 +103,36 @@ class _SectionPageView extends StatelessWidget {
103 ///视频类型 103 ///视频类型
104 ///获取视频课程内容 104 ///获取视频课程内容
105 if (state.courseType == 1) { 105 if (state.courseType == 1) {
106 - AudioPlayerUtil.getInstance() 106 + await AudioPlayerUtil.getInstance()
107 .playAudio(AudioPlayerUtilType.musicTime); 107 .playAudio(AudioPlayerUtilType.musicTime);
108 } else { 108 } else {
109 - AudioPlayerUtil.getInstance() 109 + await AudioPlayerUtil.getInstance()
110 .playAudio(AudioPlayerUtilType.videoTime); 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 return; 115 return;
118 } 116 }
119 117
120 if (state.courseType == SectionType.pictureBook.value) { 118 if (state.courseType == SectionType.pictureBook.value) {
121 - AudioPlayerUtil.getInstance() 119 + await AudioPlayerUtil.getInstance()
122 .playAudio(AudioPlayerUtilType.readingTime); 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 return; 138 return;
@@ -144,22 +140,20 @@ class _SectionPageView extends StatelessWidget { @@ -144,22 +140,20 @@ class _SectionPageView extends StatelessWidget {
144 140
145 if (state.courseType == SectionType.practice.value) { 141 if (state.courseType == SectionType.practice.value) {
146 //练习 142 //练习
147 - AudioPlayerUtil.getInstance() 143 + await AudioPlayerUtil.getInstance()
148 .playAudio(AudioPlayerUtilType.quizTime); 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 return; 158 return;
165 } 159 }
lib/utils/audio_player_util.dart
1 import 'package:audioplayers/audioplayers.dart'; 1 import 'package:audioplayers/audioplayers.dart';
  2 +import 'package:flutter/cupertino.dart';
2 import 'package:wow_english/common/extension/string_extension.dart'; 3 import 'package:wow_english/common/extension/string_extension.dart';
3 4
4 enum AudioPlayerUtilType { 5 enum AudioPlayerUtilType {
@@ -18,15 +19,18 @@ enum AudioPlayerUtilType { @@ -18,15 +19,18 @@ enum AudioPlayerUtilType {
18 final String path; 19 final String path;
19 } 20 }
20 21
21 -class AudioPlayerUtil { 22 +class AudioPlayerUtil extends WidgetsBindingObserver {
22 static AudioPlayerUtil? _instance; 23 static AudioPlayerUtil? _instance;
23 - late AudioPlayer audioPlayer; 24 + late AudioPlayer _audioPlayer;
24 late AudioPlayerUtilType currentType; 25 late AudioPlayerUtilType currentType;
  26 + bool _wasPlaying = false;
25 27
26 // 私有构造函数 28 // 私有构造函数
27 AudioPlayerUtil._internal() { 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 if (event == PlayerState.completed) { 34 if (event == PlayerState.completed) {
31 // 播放结束再次播放 35 // 播放结束再次播放
32 if (currentType == AudioPlayerUtilType.inMyTummy) { 36 if (currentType == AudioPlayerUtilType.inMyTummy) {
@@ -56,26 +60,46 @@ class AudioPlayerUtil { @@ -56,26 +60,46 @@ class AudioPlayerUtil {
56 Future<void> playAudio(AudioPlayerUtilType type) async { 60 Future<void> playAudio(AudioPlayerUtilType type) async {
57 currentType = type; 61 currentType = type;
58 String path = type.path; 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 // stop 67 // stop
64 - void stop() {  
65 - audioPlayer.stop(); 68 + Future<void> stop() async {
  69 + _audioPlayer.stop();
66 } 70 }
67 71
68 // pause 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 // resume 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 }