Search code examples
flutterjust-audioaudio-service

Audio_service '_cacheManager == null': is not true after app restart


I need to restart application with audio_service. when user sign out app should clear cache and restart. after restart when trying to reinitialise audio service I've got an error:

flutter: 'package:audio_service/audio_service.dart': Failed assertion: line 993 pos 12: '_cacheManager == null': is not true.

and AudioService can not be initialised thus.

After splash screen on the player screen we can start/pause audio and restart application. To restart app from player screen on button tap I am doing:

 FilledButton(
                onPressed: () {
                  BlocProvider.of<PlayerCubit>(context).clearCache();
                  RestartWidget.restartApp(context);
                },
                child: const Text('Restart App'),
              ),

Other code:

Future<void> runAudioApp() async {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([
    DeviceOrientation.portraitUp,
    DeviceOrientation.portraitDown,
  ]);

  await _initServices();

  runApp(
    const RestartWidget(
      onRestart: _initServices,
      child: AudioApp(),
    ),
  );
}

Future<void> _initServices() async {
  print('RestartWidget _initServices()');
  await di.init();
}

where

class RestartWidget extends StatefulWidget {
  const RestartWidget({
    required this.onRestart,
    required this.child,
    super.key,
  });

  final Widget child;
  final void Function() onRestart;

  static void restartApp(BuildContext context) {
    context.findAncestorStateOfType<_RestartWidgetState>()?.restartApp();
  }

  @override
  State<RestartWidget> createState() => _RestartWidgetState();
}

class _RestartWidgetState extends State<RestartWidget> {
  Key key = UniqueKey();

  void restartApp() {
    setState(() {
      widget.onRestart.call();
      key = UniqueKey();
    });
  }

  @override
  Widget build(BuildContext context) => KeyedSubtree(
        key: key,
        child: widget.child,
      );
}

injection_container contain:

final sl = GetIt.instance;

Future<void> init() async {
  await sl.reset();
  try {
    sl.registerSingleton(AppAudioCache());
    if (!sl.isRegistered<AppAudioHandler>()) {
      try {
        sl.registerSingleton<AppAudioHandler>(
          await initAudioService(audioCache: sl()),
        );
      } catch (e) {
        print(e);
      }
    }
  } catch (e) {
    print(e);
  }
  await sl.allReady();
}

Here is repo with a minimal reproduction project: two screens splash and player.

I have seen this issue.

and I have seen in the Audio Handler documentation:

"Register the app's AudioHandler with configuration options. This must be called once during the app's initialisation ... " so does it mean that it is impossible to initialise new instance of AudioService on application restart? maybe there is a way to completely destroy the audio service instance in order to create a new one? perhaps the restart itself was done incorrectly? thanks in advance for any help!

enter image description here


Solution

  • The way to interpret the documentation is that app initialisation only happens once when the Flutter engine itself is created and it enters your main method for the first time. So merely creating a method called restartApp and calling it multiple times does not mean you can make app initialisation happen more than once. So you can't initialise audio_service more than once in the same launch of your app.

    And since you are passing in exactly the same init parameters each time you're trying to call it, there is actually no need for you to initialise twice. If all you're trying to do is create an artificial method restartApp with some application logic in it, you can create such a custom method inside your existing audio handler instance and call it rather than re-initialising the whole system.