From d9ee3fbec68eb61577261c72f978325f5894dfce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Hirsch?= Date: Wed, 2 Apr 2025 21:04:01 +0200 Subject: [PATCH] fix: properly dispose VideoPlayerController This ensures that a playing video stops playing when we navigate away from the chat. I also reorganized the code a little. --- lib/pages/chat/events/video_player.dart | 63 ++++++++++++++----------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/lib/pages/chat/events/video_player.dart b/lib/pages/chat/events/video_player.dart index ad3dfaf15..59bb06694 100644 --- a/lib/pages/chat/events/video_player.dart +++ b/lib/pages/chat/events/video_player.dart @@ -37,22 +37,30 @@ class EventVideoPlayer extends StatefulWidget { } class EventVideoPlayerState extends State { - ChewieController? _chewieManager; + ChewieController? _chewieController; + VideoPlayerController? _videoPlayerController; bool _isDownloading = false; - String? _networkUri; - File? _tmpFile; void _downloadAction() async { if (PlatformInfos.isDesktop) { widget.event.saveFile(context); return; } + setState(() => _isDownloading = true); + try { final videoFile = await widget.event.downloadAndDecryptAttachment(); + + // Dispose the controllers if we already have them. + _disposeControllers(); + late VideoPlayerController videoPlayerController; + + // Create the VideoPlayerController from the contents of videoFile. if (kIsWeb) { final blob = html.Blob([videoFile.bytes]); - _networkUri = html.Url.createObjectUrlFromBlob(blob); + final networkUri = Uri.parse(html.Url.createObjectUrlFromBlob(blob)); + videoPlayerController = VideoPlayerController.networkUrl(networkUri); } else { final tempDir = await getTemporaryDirectory(); final fileName = Uri.encodeComponent( @@ -62,25 +70,19 @@ class EventVideoPlayerState extends State { if (await file.exists() == false) { await file.writeAsBytes(videoFile.bytes); } - _tmpFile = file; - } - final tmpFile = _tmpFile; - final networkUri = _networkUri; - if (kIsWeb && networkUri != null && _chewieManager == null) { - _chewieManager ??= ChewieController( - videoPlayerController: - VideoPlayerController.networkUrl(Uri.parse(networkUri)), - autoPlay: true, - autoInitialize: true, - ); - } else if (!kIsWeb && tmpFile != null && _chewieManager == null) { - _chewieManager ??= ChewieController( - useRootNavigator: false, - videoPlayerController: VideoPlayerController.file(tmpFile), - autoPlay: true, - autoInitialize: true, - ); + videoPlayerController = VideoPlayerController.file(file); } + _videoPlayerController = videoPlayerController; + + await videoPlayerController.initialize(); + + // Create a ChewieController on top. + _chewieController = ChewieController( + videoPlayerController: videoPlayerController, + useRootNavigator: !kIsWeb, + autoPlay: true, + autoInitialize: true, + ); } on IOException catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( @@ -90,15 +92,20 @@ class EventVideoPlayerState extends State { } catch (e, s) { ErrorReporter(context, 'Unable to play video').onErrorCallback(e, s); } finally { - // Workaround for Chewie needs time to get the aspectRatio - await Future.delayed(const Duration(milliseconds: 100)); setState(() => _isDownloading = false); } } + void _disposeControllers() { + _chewieController?.dispose(); + _videoPlayerController?.dispose(); + _chewieController = null; + _videoPlayerController = null; + } + @override void dispose() { - _chewieManager?.dispose(); + _disposeControllers(); super.dispose(); } @@ -118,7 +125,7 @@ class EventVideoPlayerState extends State { const width = 300.0; - final chewieManager = _chewieManager; + final chewieController = _chewieController; return Column( mainAxisSize: MainAxisSize.min, spacing: 8, @@ -128,8 +135,8 @@ class EventVideoPlayerState extends State { borderRadius: BorderRadius.circular(AppConfig.borderRadius), child: SizedBox( height: width, - child: chewieManager != null - ? Center(child: Chewie(controller: chewieManager)) + child: chewieController != null + ? Center(child: Chewie(controller: chewieController)) : Stack( children: [ if (hasThumbnail)