Reputation: 1355
I have a Flutter app with multiple audio and video players (e.g., video_player, just_audio, audioplayers). When the app lifecycle changes (e.g., the app goes to the background or is detached), I want to stop or pause all running media streams without manually managing each audio or video instance.
Is there a way to globally stop all audio and video streams in Flutter with a single call or a more efficient approach than tracking individual player instances?
What I’ve Tried:
Managing a list of active players and pausing each one in didChangeAppLifecycleState(). Using lifecycle hooks with manual player management, but this becomes cumbersome with many players. Desired Outcome:
A simple or global way to pause/stop all active media streams when the app's state changes. Additional Information:
My app uses these media-related packages: [list your packages, e.g., video_player, just_audio, etc.]. I’m open to using platform-specific solutions (Android/iOS) if necessary. How can I achieve this?
Upvotes: 0
Views: 35
Reputation: 74
Without the silver bullet, I didn't find a way to stop all audio playback. But this can be solved indirectly through a subscription model.
1.Implement the audio singleton class AudioSessionManager
import 'dart:io';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/foundation.dart';
class AudioSessionManager {
static final AudioSessionManager _instance = AudioSessionManager._();
AudioSessionManager._();
factory AudioSessionManager() => _instance;
/// 监听列表(实现音频统一管理)
final List<AudioSessionSoundPlayerModel> _listeners = [];
// 添加监听
void addListener(AudioSessionSoundPlayerModel cb) {
if (_listeners.contains(cb)) {
return;
}
_listeners.add(cb);
}
// 移除监听
void removeListener(AudioSessionSoundPlayerModel cb) {
_listeners.remove(cb);
}
// 通知所有监听器
Future<void> notifyListeners(Future<void> Function(AudioSessionSoundPlayerModel e) action) async {
for (var ltr in _listeners) {
await action(ltr);
}
}
void clearListeners() {
_listeners.clear();
}
}
class AudioSessionSoundPlayerModel {
AudioSessionSoundPlayerModel({
this.data,
this.onPlay,
this.onStop,
});
/// 唯一值
Map<String, dynamic>? data;
/// 播放
Future<void> Function()? onPlay;
/// 停止播放
Future<void> Function()? onStop;
AudioSessionSoundPlayerModel.fromJson(Map<String, dynamic>? json) {
if (json == null) {
return;
}
data = json['data'] ?? {};
onPlay = json['onPlay'];
onStop = json['onStop'];
}
Map<String, dynamic> toJson() {
final data = Map<String, dynamic>();
data['data'] = data;
data['onPlay'] = onPlay.hashCode;
data['onStop'] = onStop.hashCode;
return data;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
final isEqual = other is AudioSessionSoundPlayerModel &&
runtimeType == other.runtimeType &&
mapEquals(toJson(), other.toJson());
return isEqual;
}
@override
int get hashCode => data.hashCode ^ onPlay.hashCode ^ onStop.hashCode;
}
2.example
class SoundPlayerAndRecorderState extends State<SoundPlayerAndRecorder>
with AutomaticKeepAliveClientMixin, SafeSetStateMixin {
/// current audio model
AudioSessionSoundPlayerModel get audioSessionSoundPlayerModel => AudioSessionSoundPlayerModel(
data: widget.model?.toJson(),
onPlay: onPlay,
onStop: onStop,
);
...
Future<void> onPlay() async {
await AudioSessionManager().notifyListeners((e) async {
await e.onStop?.call();
});
AudioSessionManager().addListener(audioSessionSoundPlayerModel);
//your audio play codes
}
Future<void> onStop() async {
//your audio stop play codes
AudioSessionManager().removeListener(audioSessionSoundPlayerModel);
}
...
}
3.use
...
switch (state) {
case AppLifecycleState.inactive:
{
AudioSessionManager().notifyListeners((e) async {
await e.onStop?.call();
});
}
break;
default:
debugPrint("$state");
}
...
Upvotes: 0