audioDeviceStream property

  1. @override
Stream<AudioDeviceInfo> get audioDeviceStream
override

Stream of audio device changes

Emits a new AudioDeviceInfo whenever the audio output device changes, including when devices are connected/disconnected or Bluetooth state changes.

Implementation

@override
Stream<AudioDeviceInfo> get audioDeviceStream {
  _eventStream ??= eventChannel
      .receiveBroadcastStream()
      .map((event) {
        try {
          if (event is Map) {
            final info = AudioDeviceInfo.fromMap(event);
            _lastKnown = info;
            return info;
          }

          debugPrint('Unexpected event type: ${event.runtimeType}');
          const info = AudioDeviceInfo(
            type: AudioDeviceType.unknown,
            name: 'Unknown device',
          );
          _lastKnown = info;
          return info;
        } catch (e) {
          debugPrint('Error parsing audio device info: $e');
          const info = AudioDeviceInfo(
            type: AudioDeviceType.unknown,
            name: 'Unknown device',
          );
          _lastKnown = info;
          return info;
        }
      })
      .handleError((error) {
        debugPrint('Audio device stream error: $error');
      });

  // Important: EventChannel's onListen only fires for the FIRST Dart listener.
  // If another page adds a new listener while the stream is already active,
  // it may never receive the initial "current device" event. To fix that,
  // replay the last known value (or fetch it) for each listener.
  return Stream.multi((controller) {
    final cached = _lastKnown;
    if (cached != null) {
      controller.add(cached);
    } else {
      getCurrentDevice()
          .then((value) {
            _lastKnown = value;
            controller.add(value);
          })
          .catchError((_) {
            // Ignore; the event stream below may still emit.
          });
    }

    final sub = _eventStream!.listen(
      controller.add,
      onError: controller.addError,
    );
    controller.onCancel = sub.cancel;
  });
}