adds buffering intermediate state
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
+14
-3
@@ -58,6 +58,17 @@ class _MainPageState extends State<MainPage> {
|
|||||||
StreamingAudioBusinessObject get _audio => widget.audio;
|
StreamingAudioBusinessObject get _audio => widget.audio;
|
||||||
late final WebViewController _webViewController;
|
late final WebViewController _webViewController;
|
||||||
|
|
||||||
|
String _playbackStatusLabel() {
|
||||||
|
switch (_audio.playbackStatus) {
|
||||||
|
case StreamingPlaybackStatus.stopped:
|
||||||
|
return 'Stopped';
|
||||||
|
case StreamingPlaybackStatus.buffering:
|
||||||
|
return 'Buffering...';
|
||||||
|
case StreamingPlaybackStatus.playing:
|
||||||
|
return 'Playing';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -193,17 +204,17 @@ class _MainPageState extends State<MainPage> {
|
|||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _audio.isPlaying ? null : _play,
|
onPressed: _audio.isStopped ? _play : null,
|
||||||
child: const Text('▶️'),
|
child: const Text('▶️'),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 12),
|
const SizedBox(width: 12),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
onPressed: _audio.isPlaying ? _stop : null,
|
onPressed: _audio.isStopped ? null : _stop,
|
||||||
child: const Text('⏹️'),
|
child: const Text('⏹️'),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
Text(
|
||||||
_audio.isPlaying ? 'Playing' : 'Stopped',
|
_playbackStatusLabel(),
|
||||||
style: TextStyle(color: themeColors.statusText),
|
style: TextStyle(color: themeColors.statusText),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -5,18 +5,14 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:just_audio_background/just_audio_background.dart';
|
import 'package:just_audio_background/just_audio_background.dart';
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
|
enum StreamingPlaybackStatus { stopped, buffering, playing }
|
||||||
|
|
||||||
class StreamingAudioBusinessObject extends ChangeNotifier {
|
class StreamingAudioBusinessObject extends ChangeNotifier {
|
||||||
StreamingAudioBusinessObject({
|
StreamingAudioBusinessObject({
|
||||||
this.streamUrl = 'https://kryz.out.airtime.pro/kryz_a',
|
this.streamUrl = 'https://kryz.out.airtime.pro/kryz_a',
|
||||||
}) {
|
}) {
|
||||||
_playerStateSubscription = _player.playerStateStream.listen((state) {
|
_playerStateSubscription = _player.playerStateStream.listen((state) {
|
||||||
final isCurrentlyPlaying =
|
_setPlaybackStatus(_mapPlayerStateToStatus(state));
|
||||||
state.playing && state.processingState != ProcessingState.completed;
|
|
||||||
|
|
||||||
if (_isPlaying != isCurrentlyPlaying) {
|
|
||||||
_isPlaying = isCurrentlyPlaying;
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,10 +20,43 @@ class StreamingAudioBusinessObject extends ChangeNotifier {
|
|||||||
final AudioPlayer _player = AudioPlayer();
|
final AudioPlayer _player = AudioPlayer();
|
||||||
late final StreamSubscription<PlayerState> _playerStateSubscription;
|
late final StreamSubscription<PlayerState> _playerStateSubscription;
|
||||||
bool _isSourceLoaded = false;
|
bool _isSourceLoaded = false;
|
||||||
bool _isPlaying = false;
|
StreamingPlaybackStatus _playbackStatus = StreamingPlaybackStatus.stopped;
|
||||||
bool _isAudioSessionConfigured = false;
|
bool _isAudioSessionConfigured = false;
|
||||||
|
bool _playRequested = false;
|
||||||
|
|
||||||
bool get isPlaying => _isPlaying;
|
StreamingPlaybackStatus get playbackStatus => _playbackStatus;
|
||||||
|
bool get isPlaying => _playbackStatus == StreamingPlaybackStatus.playing;
|
||||||
|
bool get isBuffering => _playbackStatus == StreamingPlaybackStatus.buffering;
|
||||||
|
bool get isStopped => _playbackStatus == StreamingPlaybackStatus.stopped;
|
||||||
|
|
||||||
|
StreamingPlaybackStatus _mapPlayerStateToStatus(PlayerState state) {
|
||||||
|
if (state.processingState == ProcessingState.completed) {
|
||||||
|
return StreamingPlaybackStatus.stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.playing) {
|
||||||
|
if (state.processingState == ProcessingState.ready) {
|
||||||
|
return StreamingPlaybackStatus.playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StreamingPlaybackStatus.buffering;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_playRequested && state.processingState != ProcessingState.completed) {
|
||||||
|
return StreamingPlaybackStatus.buffering;
|
||||||
|
}
|
||||||
|
|
||||||
|
return StreamingPlaybackStatus.stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setPlaybackStatus(StreamingPlaybackStatus status) {
|
||||||
|
if (_playbackStatus == status) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_playbackStatus = status;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _configureAudioSessionIfNeeded() async {
|
Future<void> _configureAudioSessionIfNeeded() async {
|
||||||
if (_isAudioSessionConfigured) {
|
if (_isAudioSessionConfigured) {
|
||||||
@@ -40,8 +69,11 @@ class StreamingAudioBusinessObject extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> play() async {
|
Future<void> play() async {
|
||||||
|
_playRequested = true;
|
||||||
await _configureAudioSessionIfNeeded();
|
await _configureAudioSessionIfNeeded();
|
||||||
|
_setPlaybackStatus(StreamingPlaybackStatus.buffering);
|
||||||
|
|
||||||
|
try {
|
||||||
if (!_isSourceLoaded) {
|
if (!_isSourceLoaded) {
|
||||||
await _player.setAudioSource(
|
await _player.setAudioSource(
|
||||||
AudioSource.uri(
|
AudioSource.uri(
|
||||||
@@ -60,9 +92,16 @@ class StreamingAudioBusinessObject extends ChangeNotifier {
|
|||||||
final session = await AudioSession.instance;
|
final session = await AudioSession.instance;
|
||||||
await session.setActive(true);
|
await session.setActive(true);
|
||||||
await _player.play();
|
await _player.play();
|
||||||
|
} catch (_) {
|
||||||
|
_playRequested = false;
|
||||||
|
_setPlaybackStatus(StreamingPlaybackStatus.stopped);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> stop() async {
|
Future<void> stop() async {
|
||||||
|
_playRequested = false;
|
||||||
|
_setPlaybackStatus(StreamingPlaybackStatus.stopped);
|
||||||
await _player.stop();
|
await _player.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user