Almost a day of figuring way how to do it and I need your help.
I've created an app that would have a state with an instance of audioplayers
and using flutter_bloc
.
MusicPlayer
doesn't rebuild the widget with the BlocBuilder
currentPosition
and duration
of the music I'm playing and displaying it using LinearPercentIndicator
from linear_percent_indicator
package but can't seem to find a solution because rebuild doesn't work.Here's what I have so far:
class AudioPlayerBloc extends Bloc<AudioPlayerEvents, MusicPlayerState> {
@override
MusicPlayerState get initialState => MusicPlayerState(
player: AudioPlayer(),
episode: Episode(),
);
@override
Stream<MusicPlayerState> mapEventToState(AudioPlayerEvents event) async* {
if (event is InitializePlayer) {
this.currentState.episode = event.episode;
this.dispatch(PlayPlayer());
yield this.currentState;
}
if (event is PlayPlayer) {
this.play(this.currentState.episode.source);
}
if (event is PlayRemote) {
this.currentState.player.stop();
this.currentState.player.play(event.remoteURL);
yield this.currentState;
}
if (event is ShowPlayer) {
yield this.currentState;
}
if (event is HidePlayer) {
yield this.currentState;
}
}
void play(String remoteURL) {
this.dispatch(PlayRemote(remoteURL));
}
void stop() async {
await this.currentState.player.stop();
}
void pause() async{
await this.currentState.player.pause();
}
void resume(){
this.currentState.player.resume();
}
@override
void dispose() {
super.dispose();
}
}
abstract class AudioPlayerEvents {}
class InitializePlayer extends AudioPlayerEvents {
Episode episode;
InitializePlayer(this.episode);
}
class PlayRemote extends AudioPlayerEvents {
final String remoteURL;
PlayRemote(this.remoteURL);
}
class PlayPlayer extends AudioPlayerEvents {}
class ShowPlayer extends AudioPlayerEvents {}
class HidePlayer extends AudioPlayerEvents {}
import 'package:audioplayers/audioplayers.dart';
class MusicPlayerState {
AudioPlayer player;
Episode episode; // My Custom Class
MusicPlayerState({
this.player,
this.episode,
});
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: MaterialApp(
navigatorObservers: [],
initialRoute: HomeScreen.id,
routes: {
HomeScreen.id: (context) => HomeScreen(app: widget.app),
},
),
);
}
}
class MusicPlayer extends StatelessWidget {
@override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
return BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
bloc: audioPlayerBloc,
builder: (context, state) {
return Container(
height: 200.0,
color: Colors.cyan[200],
child: Padding(
padding: const EdgeInsets.only(top: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Column(
children: <Widget>[
Text("${state.episode.name}"),
Row(
children: <Widget>[
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getCurrentPosition(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
Expanded(
flex: 3,
child: LinearPercentIndicator(
lineHeight: 7.0,
// percent: this.currentPosition / this.fileDuration,
percent: 0.3,
backgroundColor: Colors.grey,
progressColor: Colors.blue,
),
),
Expanded(
flex: 1,
child: FutureBuilder<int>(
future: audioPlayerBloc.currentState.player.getDuration(),
builder: (context, AsyncSnapshot<int> snapshot) {
double seconds = snapshot.data / 1000;
if (snapshot.hasData) {
return Text("${printDuration(Duration(seconds: seconds.toInt()), abbreviated: true)}");
} else {
return Text('Calculating..');
}
},
),
),
],
),
Text(state.player.state.toString()),
FlatButton(
onPressed: () {
print('close here');
Navigator.of(context).pop();
},
child: Icon(
Icons.close,
color: Colors.black.withOpacity(0.5),
),
),
Row(
children: <Widget>[
FlatButton(
onPressed: () {
audioPlayerBloc.pause();
},
child: Text('Pause Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.resume();
},
child: Text('Resume Player'),
),
FlatButton(
onPressed: () {
audioPlayerBloc.stop();
},
child: Text('Stop Player'),
),
],
)
],
)
],
),
),
);
},
);
}
}
@override
Widget build(BuildContext context) {
final AudioPlayerBloc audioPlayerBloc = BlocProvider.of<AudioPlayerBloc>(context);
return MultiBlocProvider(
providers: [
BlocProvider<AudioPlayerBloc>(
builder: (_) => AudioPlayerBloc(),
)
],
child: Scaffold(
appBar: AppBar(
title: Text('Global Audio Player'),
),
body: Container(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return Column(
children: <Widget>[
Flexible(
child: getListView(context)
),
displayPlayer(state.player), // Here I'm trying to display the player when the AudioPlayerState is PLAYING
],
);
},
),
),
),
);
}
Widget displayPlayer(AudioPlayer player){
return MusicPlayer();
if(player.state == AudioPlayerState.PLAYING) {
return MusicPlayer();
}
return Container();
}
class _EpisodesScreenState extends State<EpisodesScreen> {
@override
void initState() {
super.initState();
print(widget.series);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.series.name)),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 0,
child: Image.network(widget.series.image.original),
),
Expanded(
child: getListView(context),
),
Expanded(
child: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return displayPlayer(state.player);
},
),
)
],
));
}
Widget getListView(BuildContext context) {
List<Episode> episodesList = widget.series.episodes;
final AudioPlayerBloc audioPlayerBloc =
BlocProvider.of<AudioPlayerBloc>(context);
var listView = ListView.separated(
itemCount: episodesList.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(episodesList[index].name),
trailing: BlocBuilder<AudioPlayerBloc, MusicPlayerState>(
builder: (context, state) {
return FlatButton(
onPressed: () {
audioPlayerBloc.dispatch(InitializePlayer(episodesList[index]));
},
child: Icon(
Icons.play_arrow,
color: Colors.black87,
),
);
},
),
);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(
height: 1.0,
color: Colors.black12,
);
},
);
return listView;
}
}
I missed the yield *
part of it and it is now working.
Stream<Duration> currentPosition() async* {
yield* this.currentState.audioPlayer.onAudioPositionChanged;
}
Stream<Duration> fileDuration() async* {
yield* this.currentState.audioPlayer.onDurationChanged;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With