0

I have a song playlist screen that includes a list of songs and a navbar for audio play/pause. I set the initial value of the song field as the first element of the songs list, however, when I update the selected song with setState(), the navbar does not update its song.

class Body extends StatefulWidget {
  @override
  BodyState createState() => BodyState();
}

class BodyState extends State<Body> {
  Music selectedSong = songs[0];
  ...
  @override
  Widget build(BuildContext context) {
    return Column(
       ...
       Expanded(
          child: ListView.builder(
          shrinkWrap: true,
          scrollDirection: Axis.vertical,
          padding: const EdgeInsets.only(top: 20),
          itemCount: songs.length,
          itemBuilder: (context, i) => songCard(songs[i])
       ...
       MusicBar(music: selectedSong)

For songCard() function:

 Widget songCard(Music item) {
    return ListTile(
      onTap: () => {
        setState(() {
          selectedSong = item;
        })
      },
     ...

When I debug the code, selectedSong field is updated as expected, however music field inside MusicBar is not updated.

For MusicBar class:

class MusicBar extends StatefulWidget {
  final Music music;
  const MusicBar({Key key, this.music}) : super(key: key);
  MusicBarState createState() => MusicBarState(music: music);
}

class MusicBarState extends State<MusicBar> {

  final Music music;
  MusicBarState({this.music});

  AudioPlayer player = AudioPlayer();

  @override
  void initState() {
    super.initState();
    // duration = music.duration;
    player.onDurationChanged.listen((updatedDuration) {
      setState(() {
        duration = updatedDuration;
      });
    });

    player.onAudioPositionChanged.listen((updatedPosition) {
        setState(() {
          position = updatedPosition;
        });
    });
  }

  @override
  void dispose(){
    super.dispose();
    player.stop();
    player.dispose();
  }
  
  @override
  Widget build(BuildContext context){
    return Container(
      ...

What did I do wrong so that the MusicBar widget is not updating? If needed, songs list is defined outside Body class, and I am using audioplayers library

1 Answers1

1

In MusicBarState you can access to music field on Music using widget. You can refactor you State to:

class MusicBar extends StatefulWidget {
  final Music music;
  const MusicBar({Key key, this.music}) : super(key: key);
  MusicBarState createState() => MusicBarState(); // Empty constructor
}

class MusicBarState extends State<MusicBar> {
  // Remove this
  // final Music music;
  // MusicBarState({this.music});

 someMethod() {
   // You can access to music using widget:
   print(widget.music);
 }
 // ...
}

Test and check if now works

Ignacior
  • 897
  • 2
  • 15
  • how does this differ? I want to understand the logic behind it. – nondefinite Apr 15 '21 at 21:15
  • The build method render all `widgets` after setState is called. If you pass a music reference to StatefulWidget and then to State, only reference it's passed and State cannot view changes in your music reference in the widget. The createState method only calls when the widget has a create new state and not when his parents are updated. If you use `widget`, all times a MusicBar rebuilding you can make sure that you are always taking the new references that are passed to the widget and not to the state – Ignacior Apr 15 '21 at 21:19
  • Oh thanks! But how can I detect if widget.music changed? Because I need to stop playing and reset the slider so that I can play the new song. Is there any function that I can listen StatefulWidget changes in State? – nondefinite Apr 15 '21 at 21:27
  • You can try to implement some pattern like `Observer` in your` Music` model. You could also use a state manager (like Provider, Bloc, etc) to manipulate the state of the current music if you are going to use it in different parts or globally. – Ignacior Apr 15 '21 at 21:37
  • You can also just override the didChangeDependecies method of your navbar state. – Gaspard Merten Apr 15 '21 at 22:01
  • @GaspardMerten yeah that worked as well, thanks! – nondefinite Apr 16 '21 at 06:45