0

I'm developing the flutter app which can play audio. Audio is fetching from firebase and playing perfectly but the problem is that when begin to play audio and exit from that screen, again, revisiting that screen, it won't update its state(seekbar, play/pause button, time passed).

So far I came to know that it will easy with provider. so I have done it in that way but getting error like

======== Exception caught by foundation library ====================================================
The following assertion was thrown while dispatching notifications for PlayerStateModel:
setState() or markNeedsBuild() called during build.

Here is my code for that screen

import 'dart:ui';
import 'package:app/model/PlayerState.dart';
import 'package:app/stories/get_stories.dart';
import 'package:audio_service/audio_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:app/globals.dart' as globals;
import 'package:just_audio/just_audio.dart';
import '../widgets/error_alert.dart';
import 'package:audio_session/audio_session.dart';
import 'package:provider/provider.dart';

class PlayingStory extends StatefulWidget {
  final String story;
  final String episode;
  final String playLink;
  final String poster;

  const PlayingStory(
      {Key? key,
      required this.story,
      required this.episode,
      required this.playLink,
      required this.poster})
      : super(key: key);

  @override
  State<StatefulWidget> createState() => PlayingStoryState();
}

class PlayingStoryState extends State<PlayingStory> with WidgetsBindingObserver {

  double _value = 0.0;
  final audioPlayer = AudioPlayer();
  late bool isPlaying;
  late AppLifecycleState state;
  late String episode = widget.episode;

  void _showChoices(BuildContext context) {

    // GetStories().updateStoryList(widget.story,context);
    var nextEpisode = GetStories().fetchNextEpisodes(widget.story);

    OverlayState overlayState = Overlay.of(context);
    OverlayEntry? choice;

    final Size size = MediaQuery.of(context).size;
    final height = size.height;
    final width = size.width;

    choice = OverlayEntry(builder: (context) {
      print("_-_-_-_-_-_-_-_-_-_-_ OVERLAY CALLED _-_-_-_-_-_-_-_-_-_-_");
      return Scaffold(
        backgroundColor: Colors.transparent,
        body: Center(
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: width * 0.035, vertical: height * 0.06),
            child: ColoredBox(
              color: const Color(0xff838080),
              child: BackdropFilter(
                filter: ImageFilter.blur(
                  sigmaY: 8.0,
                  sigmaX: 8.0,
                ),
                child: Center(
                  child: ListView(
                    children: [

                      FutureBuilder<Map<dynamic, dynamic>>(
                        future: nextEpisode,
                        builder: (context, snapshot) {
                          if (!snapshot.hasData) {
                            return const Center(
                              child: CircularProgressIndicator(),
                            );
                          }

                          final nxteps = snapshot.data!;

                          return ListView.builder(
                            physics: const NeverScrollableScrollPhysics(),
                            shrinkWrap: true,
                            itemCount: nxteps.length,
                            itemBuilder: (context, index) {
                              return ListTile(
                                onTap: () async {
                                  var eps = await GetStories().playNextEpisode(widget.story, nxteps.keys.toList()[index]);
                                  setState(() {
                                    print(eps);
                                    audioInit(eps["episode_audio"]);
                                    episode = eps["episode_id"];
                                    choice!.remove();
                                  });
                                },
                                shape: RoundedRectangleBorder(
                                  borderRadius: BorderRadius.circular(5),
                                ),
                                tileColor: Colors.red,
                                title: Text(
                                  nxteps.values.toList()[index],
                                  style: const TextStyle(color: Colors.white),
                                ),
                                subtitle: Text(
                                  nxteps.keys.toList()[index],
                                  style: const TextStyle(color: Colors.white),
                                ),
                                leading: IconButton(
                                  onPressed: () {
                                    choice!.remove();
                                  },
                                  color: Colors.white,
                                  iconSize: height * 0.04,
                                  icon: const Icon(
                                    Icons.play_arrow_rounded,
                                  ),
                                ),
                              );
                            },
                          );
                        },
                      )

                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      );
    });

    overlayState.insert(choice);
  }

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    audioInit(widget.playLink);
    isPlaying = false;
  }

  Future<void> audioInit(audio) async {
    final session = await AudioSession.instance;
    await session.configure(const AudioSessionConfiguration.speech());

    audioPlayer.positionStream.listen((position) {
      if (mounted == true) {
        setState(() {
          Provider.of<PlayerStateModel>(context).setCurrentPosition(position);
          // _value = position.inSeconds.toDouble();
        });
      }
    });

    audioPlayer.playerStateStream.listen((event) {
      if(event.processingState == ProcessingState.completed) {
        if(mounted) {
          setState(() {
            // isPlaying = false;
            Provider.of<PlayerStateModel>(context).setIsPlaying(false);
            _showChoices(context);
          });
        }
        if (kDebugMode) {
          print("_-_-_-_-_-_ SONG COMPLETED _-_-_-_-_-_");
        }
      } else {
        if (kDebugMode) {
          print("_-_-_-_-_-_ SONG NOT COMPLETED _-_-_-_-_-_");
        }
      }
    });

    audioPlayer.playbackEventStream.listen((event) {
    }, onError: (Object e, StackTrace stackTrace) {
      if (kDebugMode) {
        print('A stream error occurred: $e');
      }
    });
    // Try to load audio from a source and catch any errors.
    try {
      // AAC example: https://dl.espressif.com/dl/audio/ff-16b-2c-44100hz.aac
      await audioPlayer.setAudioSource(AudioSource.uri(
        Uri.parse(audio),
        tag: MediaItem(
          // Specify a unique ID for each media item:
          id: 'StoryFy',
          // Metadata to display in the notification:
          album: widget.story,
          title: episode.split("=")[0],
          artUri: Uri.parse(widget.poster),
        ),
      ));
      audioPlayer.play();
      Provider.of<PlayerStateModel>(context).setIsPlaying(true);
      // setState(() {
      //   isPlaying = true;
      // });
    } catch (e) {
      if (kDebugMode) {
        print("Error loading audio source: $e");
      }
    }
  }

  // @override
  // void dispose() {
  //   audioPlayer.dispose();
  //   WidgetsBinding.instance.removeObserver(this);
  //   super.dispose();
  // }

  // @override
  // void didChangeAppLifecycleState(AppLifecycleState state) {
  //   super.didChangeAppLifecycleState(state);
  //   if (state == AppLifecycleState.resumed) {
  //     // Resume audio playback if necessary
  //     // if (isPlaying) {
  //     //   audioPlayer.play();
  //     // }
  //     state = state;
  //   } else if (state == AppLifecycleState.paused) {
  //     // Pause audio playback if necessary
  //     // if (isPlaying) {
  //     //   audioPlayer.pause();
  //     // }
  //   }
  // }

  String formatDuration(Duration duration) {
    final hours = duration.inHours.remainder(60).toString().padLeft(2, '0');
    final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
    final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0');
    return '$hours:$minutes:$seconds';
  }

  @override
  Widget build(BuildContext context) {
    final Size screenSize = MediaQuery.of(context).size;
    final double width = screenSize.width;
    final double height = screenSize.height;

    PlayerStateModel playerStateModel = Provider.of<PlayerStateModel>(context);
    return ChangeNotifierProvider(
      create: (_) => PlayerStateModel(),
      builder: (context, child) {
        return Scaffold(
            appBar: AppBar(),
            body: Padding(
              padding: EdgeInsets.symmetric(horizontal: width * 0.03),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Center(
                    child: Card(
                      elevation: 10,
                      child: ClipRRect(
                        borderRadius: BorderRadius.circular(10),
                        child: Image.network(
                          widget.poster,
                          height: height * 0.5,
                          width: width,
                          fit: BoxFit.fill,
                        ),
                      ),
                    ),
                  ),
                  SizedBox(
                    height: height * 0.05,
                  ),
                  Center(
                    child: Text(
                      episode.split("=")[0],
                      style: TextStyle(
                        fontSize: height * 0.03,
                      ),
                    ),
                  ),
                  SizedBox(
                    height: height * 0.01,
                  ),
                  Center(
                    child: Text(
                      widget.story,
                      style: TextStyle(
                        fontSize: height * 0.02,
                      ),
                    ),
                  ),
                  SizedBox(
                    height: height * 0.02,
                  ),
                  StreamBuilder<Duration?>(
                    stream: audioPlayer.durationStream,
                    builder: (context, snapshot) {
                      final duration = snapshot.data ?? Duration.zero;
                      playerStateModel.setDuration(duration);
                      return Slider(
                        value: _value,
                        min: 0.0,
                        max: duration.inSeconds.toDouble(),
                        inactiveColor: globals.shadowColor,
                        onChanged: (newValue) {
                          final clampedValue = newValue.clamp(0.0, duration.inSeconds.toDouble());
                          audioPlayer.seek(Duration(seconds: clampedValue.toInt()));
                        },
                      );
                    },
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      StreamBuilder<Duration>(
                        stream: audioPlayer.positionStream,
                        builder: (context, snapshot) {
                          final position = snapshot.data ?? Duration.zero;
                          playerStateModel.setCurrentPosition(position);
                          return Text(formatDuration(position));
                        },
                      ),
                      StreamBuilder<Duration?>(
                        stream: audioPlayer.durationStream,
                        builder: (context, snapshot) {
                          final duration = snapshot.data ?? Duration.zero;
                          return Text(formatDuration(duration));
                        },
                      ),
                    ],
                  ),
                  SizedBox(
                    height: height * 0.02,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        onPressed: () {
                          showAlert(context, "data.values.toList()[index]");
                          audioPlayer.seekToPrevious();
                        },
                        iconSize: height * 0.05,
                        color: Colors.storyfytheme,
                        icon: const Icon(
                          Icons.skip_previous_rounded,
                        ),
                      ),
                      IconButton(
                        onPressed: () async {
                          final currentPosition = audioPlayer.position;
                          final duration = audioPlayer.duration;
                          if (currentPosition != null && duration != null) {
                            final newPosition = currentPosition - const Duration(seconds: 10);
                            final clampedPosition = newPosition.isNegative ? Duration.zero : newPosition;
                            audioPlayer.seek(clampedPosition);
                          }
                        },
                        iconSize: height * 0.05,
                        color: Colors.storyfytheme,
                        icon: const Icon(
                          Icons.replay_10_rounded,
                        ),
                      ),
                      IconButton(
                        onPressed: () {
                          if (mounted == true) {
                            setState(() {
                              Provider.of<PlayerStateModel>(context, listen: false).setIsPlaying(!playerStateModel.isPlaying);
                              (isPlaying)
                                  ? audioPlayer.pause()
                                  : audioPlayer.play();
                              isPlaying = !isPlaying;
                            });
                          }
                        },
                        iconSize: height * 0.05,
                        color: Colors.storyfytheme,
                        icon: isPlaying
                            ? const Icon(
                          Icons.pause,
                        )
                            : const Icon(
                          Icons.play_arrow_rounded,
                        ),
                      ),
                      IconButton(
                        onPressed: () async {
                          final currentPosition = audioPlayer.position;
                          final duration = audioPlayer.duration;
                          if (currentPosition != null && duration != null) {
                            final newPosition = currentPosition + const Duration(seconds: 10);
                            final clampedPosition = newPosition.compareTo(duration) > 0 ? duration : newPosition;
                            audioPlayer.seek(clampedPosition);
                          }
                          // await audioPlayer.seek(Duration(
                          //     seconds: audioPlayer.position.inSeconds + 10));
                        },
                        iconSize: height * 0.05,
                        color: Colors.storyfytheme,
                        icon: const Icon(
                          Icons.forward_10_rounded,
                        ),
                      ),
                      IconButton(
                        onPressed: () {
                          showAlert(context, "data.values.toList()[index]");
                          audioPlayer.seekToNext();
                        },
                        iconSize: height * 0.05,
                        color: Colors.storyfytheme,
                        icon: const Icon(
                          Icons.skip_next_rounded,
                        ),
                      )
                    ],
                  )
                ],
              ),
            )
        );
      }
    );
  }
}

in above code i'm having problem with updating slider value with provider.

the provider state is as follows:

import 'package:flutter/widgets.dart';

class PlayerStateModel extends ChangeNotifier {
  bool _isPlaying = true;
  Duration _currentPosition = Duration.zero;
  Duration _duration = Duration.zero;

  bool get isPlaying => _isPlaying;
  Duration get duration => _duration;
  Duration get currentPosition => _currentPosition;

  void setIsPlaying(bool value) {
    _isPlaying = value;
    notifyListeners();
  }

  void setCurrentPosition(Duration value) {
    _currentPosition = value;
    notifyListeners();
  }

  void setDuration(Duration value) {
    _duration = value;
    notifyListeners();
  }
}```
  • You probably need to add extend WidgetsBindingObserver and override the didChangeAppLifecycleState function for this widget. – Eric Su Jul 06 '23 at 07:24
  • @EricSu you can see my code in class declaration, in initState and didChangeAppLifecycleState function. I have done it but still it not working. – Dhanrajsinh Parmar Jul 06 '23 at 08:01

0 Answers0