mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-29 12:52:13 +02:00
116 lines
3.3 KiB
Dart
116 lines
3.3 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:spotiflac_android/l10n/l10n.dart';
|
|
|
|
/// Uses the system's real audio routes and keeps native discovery in native UI.
|
|
class AudioOutputButton extends StatefulWidget {
|
|
const AudioOutputButton({super.key, this.color, this.onPickerChanged});
|
|
|
|
final Color? color;
|
|
final ValueChanged<bool>? onPickerChanged;
|
|
|
|
@override
|
|
State<AudioOutputButton> createState() => _AudioOutputButtonState();
|
|
}
|
|
|
|
class _AudioOutputButtonState extends State<AudioOutputButton> {
|
|
static const _androidChannel = MethodChannel(
|
|
'com.zarz.spotiflac/audio_output',
|
|
);
|
|
MethodChannel? _viewChannel;
|
|
bool _opening = false;
|
|
|
|
Map<String, Object> get _appearance => {
|
|
'color': (widget.color ?? Theme.of(context).colorScheme.onSurface)
|
|
.toARGB32(),
|
|
'label': context.l10n.nowPlayingAudioOutput,
|
|
};
|
|
|
|
@override
|
|
void didUpdateWidget(AudioOutputButton oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
_updateAppearance();
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
super.didChangeDependencies();
|
|
_updateAppearance();
|
|
}
|
|
|
|
void _updateAppearance() {
|
|
final channel = _viewChannel;
|
|
if (channel != null) {
|
|
unawaited(channel.invokeMethod<void>('update', _appearance));
|
|
}
|
|
}
|
|
|
|
void _attachView(int id) {
|
|
_viewChannel?.setMethodCallHandler(null);
|
|
_viewChannel = MethodChannel('com.zarz.spotiflac/audio_output/$id')
|
|
..setMethodCallHandler((call) async {
|
|
if (mounted && call.method == 'pickerChanged') {
|
|
widget.onPickerChanged?.call(call.arguments == true);
|
|
}
|
|
});
|
|
_updateAppearance();
|
|
}
|
|
|
|
Future<void> _showAndroidPicker() async {
|
|
if (_opening) return;
|
|
_opening = true;
|
|
widget.onPickerChanged?.call(true);
|
|
var opened = false;
|
|
try {
|
|
opened = await _androidChannel.invokeMethod<bool>('show') ?? false;
|
|
} on PlatformException {
|
|
// Show a visible failure instead of leaving an unresponsive control.
|
|
} on MissingPluginException {
|
|
// The native bridge is not available on unsupported builds.
|
|
} finally {
|
|
_opening = false;
|
|
if (mounted) widget.onPickerChanged?.call(false);
|
|
}
|
|
if (mounted && !opened) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text(context.l10n.nowPlayingAudioOutputUnavailable)),
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_viewChannel?.setMethodCallHandler(null);
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (kIsWeb) return const SizedBox.shrink();
|
|
switch (defaultTargetPlatform) {
|
|
case TargetPlatform.iOS:
|
|
return SizedBox.square(
|
|
dimension: 48,
|
|
child: UiKitView(
|
|
viewType: 'com.zarz.spotiflac/audio_output',
|
|
creationParams: _appearance,
|
|
creationParamsCodec: const StandardMessageCodec(),
|
|
onPlatformViewCreated: _attachView,
|
|
),
|
|
);
|
|
case TargetPlatform.android:
|
|
return IconButton(
|
|
tooltip: context.l10n.nowPlayingAudioOutput,
|
|
color: widget.color,
|
|
icon: const Icon(Icons.speaker_group_outlined),
|
|
onPressed: _showAndroidPicker,
|
|
);
|
|
default:
|
|
return const SizedBox.shrink();
|
|
}
|
|
}
|
|
}
|