From ed67fdaff801a87380b147ee272b9bedd55c196b Mon Sep 17 00:00:00 2001 From: JohnE Date: Wed, 4 Feb 2026 14:52:35 -0800 Subject: [PATCH] FIX: need to dispose of player and resources --- lib/splash_video_controller.dart | 58 ++++++++++++++++++++++++++++++-- pubspec.yaml | 2 +- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/splash_video_controller.dart b/lib/splash_video_controller.dart index 3ab5c45..a8c7931 100644 --- a/lib/splash_video_controller.dart +++ b/lib/splash_video_controller.dart @@ -32,7 +32,9 @@ class SplashVideoController { /// [loopVideo] - When true, video will loop infinitely until manually stopped SplashVideoController({ this.loopVideo = false, - }); + }) { + _activeControllers.add(this); + } /// Whether the video should loop infinitely /// @@ -45,6 +47,37 @@ class SplashVideoController { Player? _player; bool _isDisposed = false; + // Track all active controllers for cleanup during hot restart + static final Set _activeControllers = {}; + + /// Disposes all active SplashVideoController instances + /// + /// Call this before hot restart to prevent "Callback invoked after it has + /// been deleted" crashes. This stops all MPV callback activity before the + /// Dart isolate is destroyed. + /// + /// Usage in main.dart: + /// ```dart + /// // In a StatefulWidget wrapping your app: + /// @override + /// void reassemble() { + /// SplashVideoController.disposeAll(); + /// super.reassemble(); + /// } + /// ``` + static Future disposeAll() async { + final controllers = Set.from(_activeControllers); + for (final controller in controllers) { + controller.dispose(); + } + _activeControllers.clear(); + } + + /// Returns the number of active (non-disposed) controllers + /// + /// Useful for debugging resource leaks + static int get activeControllerCount => _activeControllers.length; + /// Access to the underlying media_kit Player instance /// /// Provides full control over video playback including: @@ -103,11 +136,30 @@ class SplashVideoController { /// Disposes the controller and releases resources /// - /// Should be called when the splash screen is no longer needed + /// This method pauses the player first to stop MPV callback activity, + /// then disposes after a short delay. This helps prevent crashes during + /// hot restart when the Dart isolate is destroyed while MPV's native + /// thread is still running. + /// + /// Should be called when the splash screen is no longer needed. void dispose() { if (_isDisposed) return; _isDisposed = true; - _player?.dispose(); + _activeControllers.remove(this); + + final player = _player; + if (player != null) { + // Pause first to stop callback activity before disposing + player.pause().then((_) { + // Small delay to let MPV thread settle before disposal + Future.delayed(const Duration(milliseconds: 50), () { + player.dispose(); + }); + }).catchError((_) { + // If pause fails, dispose anyway + player.dispose(); + }); + } _player = null; } diff --git a/pubspec.yaml b/pubspec.yaml index 98d0d32..0f80565 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: splash_video description: "Simple video splash screen for Flutter applications." -version: 0.1.3 +version: 0.1.6 homepage: malloc.io environment: