Mohammed Hamdan
Mohammed Hamdan

Reputation: 1355

How to globally stop all running audio and video streams during app lifecycle changes?

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

Answers (1)

SHANG
SHANG

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

Related Questions