Last active
June 20, 2022 12:06
-
-
Save scysys/7f700cd49f09ba788021504e8d3477aa to your computer and use it in GitHub Desktop.
Just for error demonstration: https://github.com/ryanheise/just_audio/issues/759
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a minimal example demonstrating live streaming. | |
// | |
// To run: | |
// | |
// flutter run -t lib/example_radio.dart | |
import 'package:audio_session/audio_session.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:just_audio/just_audio.dart'; | |
import 'package:just_audio_example/common.dart'; | |
void main() => runApp(const MyApp()); | |
class MyApp extends StatefulWidget { | |
const MyApp({Key? key}) : super(key: key); | |
@override | |
MyAppState createState() => MyAppState(); | |
} | |
class MyAppState extends State<MyApp> with WidgetsBindingObserver { | |
final _player = AudioPlayer(userAgent: 'myradioapp/1.0 (Linux;Android 11) https://myradioapp.com'); | |
@override | |
void initState() { | |
super.initState(); | |
ambiguate(WidgetsBinding.instance)!.addObserver(this); | |
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( | |
statusBarColor: Colors.black, | |
)); | |
_init(); | |
} | |
Future<void> _init() async { | |
// Inform the operating system of our app's audio attributes etc. | |
// We pick a reasonable default for an app that plays speech. | |
final session = await AudioSession.instance; | |
await session.configure(const AudioSessionConfiguration.speech()); | |
// Listen to errors during playback. | |
_player.playbackEventStream.listen((event) {}, | |
onError: (Object e, StackTrace stackTrace) { | |
print('A stream error occurred: $e'); | |
}); | |
// Try to load audio from a source and catch any errors. | |
try { | |
await _player.setAudioSource(AudioSource.uri( | |
Uri.parse("http://stream.laut.fm/hitradio1"))); | |
} catch (e) { | |
print("Error loading audio source: $e"); | |
} | |
} | |
@override | |
void dispose() { | |
ambiguate(WidgetsBinding.instance)!.removeObserver(this); | |
// Release decoders and buffers back to the operating system making them | |
// available for other apps to use. | |
_player.dispose(); | |
super.dispose(); | |
} | |
@override | |
void didChangeAppLifecycleState(AppLifecycleState state) { | |
if (state == AppLifecycleState.paused) { | |
// Release the player's resources when not in use. We use "stop" so that | |
// if the app resumes later, it will still remember what position to | |
// resume from. | |
_player.stop(); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
debugShowCheckedModeBanner: false, | |
home: Scaffold( | |
body: SafeArea( | |
child: SizedBox( | |
width: double.maxFinite, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.center, | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
StreamBuilder<IcyMetadata?>( | |
stream: _player.icyMetadataStream, | |
builder: (context, snapshot) { | |
final metadata = snapshot.data; | |
final title = metadata?.info?.title ?? ''; | |
final url = metadata?.info?.url; | |
return Column( | |
children: [ | |
if (url != null) Image.network(url), | |
Padding( | |
padding: const EdgeInsets.only(top: 8.0), | |
child: Text(title, | |
style: Theme.of(context).textTheme.headline6), | |
), | |
], | |
); | |
}, | |
), | |
// Display play/pause button and volume/speed sliders. | |
ControlButtons(_player), | |
], | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
/// Displays the play/pause button and volume/speed sliders. | |
class ControlButtons extends StatelessWidget { | |
final AudioPlayer player; | |
const ControlButtons(this.player, {Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Row( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
/// This StreamBuilder rebuilds whenever the player state changes, which | |
/// includes the playing/paused state and also the | |
/// loading/buffering/ready state. Depending on the state we show the | |
/// appropriate button or loading indicator. | |
StreamBuilder<PlayerState>( | |
stream: player.playerStateStream, | |
builder: (context, snapshot) { | |
final playerState = snapshot.data; | |
final processingState = playerState?.processingState; | |
final playing = playerState?.playing; | |
if (processingState == ProcessingState.loading || | |
processingState == ProcessingState.buffering) { | |
return Container( | |
margin: const EdgeInsets.all(8.0), | |
width: 64.0, | |
height: 64.0, | |
child: const CircularProgressIndicator(), | |
); | |
} else if (playing != true) { | |
return IconButton( | |
icon: const Icon(Icons.play_arrow), | |
iconSize: 64.0, | |
onPressed: player.play, | |
); | |
} else if (processingState != ProcessingState.completed) { | |
return IconButton( | |
icon: const Icon(Icons.pause), | |
iconSize: 64.0, | |
onPressed: player.pause, | |
); | |
} else { | |
return IconButton( | |
icon: const Icon(Icons.replay), | |
iconSize: 64.0, | |
onPressed: () => player.seek(Duration.zero), | |
); | |
} | |
}, | |
), | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment