From eaa8ce7a39551d5d77bf6a41f34ae1e1fd62e767 Mon Sep 17 00:00:00 2001 From: Roscoe Rubin-Rottenberg Date: Tue, 26 May 2026 19:18:28 -0400 Subject: [PATCH] fix: remove stutter on video start --- .../ui/widgets/post/feed_post_widget.dart | 7 -- .../feed/ui/widgets/videos/video_player.dart | 74 ++++++++++++++++++- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/lib/src/features/feed/ui/widgets/post/feed_post_widget.dart b/lib/src/features/feed/ui/widgets/post/feed_post_widget.dart index 0c82d45..9653a7d 100644 --- a/lib/src/features/feed/ui/widgets/post/feed_post_widget.dart +++ b/lib/src/features/feed/ui/widgets/post/feed_post_widget.dart @@ -186,13 +186,6 @@ class _FeedPostWidgetState extends ConsumerState { ); } - // If user is not on feeds tab, show empty container to dispose video - if (!isOnFeedsTab) { - return const DecoratedBox( - decoration: BoxDecoration(color: AppColors.black), - ); - } - return FutureBuilder( future: _postFuture, builder: (context, snapshot) { diff --git a/lib/src/features/feed/ui/widgets/videos/video_player.dart b/lib/src/features/feed/ui/widgets/videos/video_player.dart index 91a2ec8..453cfaf 100644 --- a/lib/src/features/feed/ui/widgets/videos/video_player.dart +++ b/lib/src/features/feed/ui/widgets/videos/video_player.dart @@ -47,6 +47,7 @@ class PostVideoPlayerState extends ConsumerState with TickerProviderStateMixin { BetterPlayerController? videoController; bool _userInteracted = false; + bool _showThumbnailOverlay = true; late AnimationController _bounceController; late Animation _bounceAnimation; @@ -91,6 +92,22 @@ class PostVideoPlayerState extends ConsumerState super.dispose(); } + @override + void didUpdateWidget(PostVideoPlayer oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.videoUrl != widget.videoUrl) { + _showThumbnailOverlay = true; + } + } + + void _hideThumbnailOverlay() { + if (!_showThumbnailOverlay || !mounted) return; + + setState(() { + _showThumbnailOverlay = false; + }); + } + void _videoListener(BetterPlayerEvent event) { if (mounted) { final paused = event.betterPlayerEventType == BetterPlayerEventType.pause; @@ -107,6 +124,15 @@ class PostVideoPlayerState extends ConsumerState ..stop() ..value = 1.0; } + + final progress = + event.betterPlayerEventType == BetterPlayerEventType.progress; + final progressPosition = event.parameters?['progress']; + if (progress && + progressPosition is Duration && + progressPosition > Duration.zero) { + _hideThumbnailOverlay(); + } } } @@ -172,12 +198,24 @@ class PostVideoPlayerState extends ConsumerState } } - void _handleNavigationPause(bool isOnFeedsTab) { + void _handleNavigationVisibility( + bool isOnFeedsTab, { + required bool shouldPlay, + required bool isFeedSettingsVisible, + }) { if (!isInitialized) return; // Always pause when not on feeds tab, regardless of user interaction if (!isOnFeedsTab && isPlaying) { videoController?.pause(); + return; + } + + if (isOnFeedsTab) { + _handleAutoPlayPause( + shouldPlay, + isFeedSettingsVisible: isFeedSettingsVisible, + ); } } @@ -245,7 +283,19 @@ class PostVideoPlayerState extends ConsumerState _lastNavigationIndex = navigationState.currentIndex; WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { - _handleNavigationPause(isOnFeedsTab); + final shouldPlay = feedState != null + ? feedState.index == widget.index + : profileFeedIndex != null && widget.index != null + ? profileFeedIndex == widget.index || + (profileFeedIndex == -1 && widget.isInitialPost) + : widget.feed == null && + widget.index == null && + widget.profileFeedUri == null; + _handleNavigationVisibility( + isOnFeedsTab, + shouldPlay: shouldPlay, + isFeedSettingsVisible: feedSettingsVisible, + ); } }); } @@ -320,6 +370,25 @@ class PostVideoPlayerState extends ConsumerState videoAspectRatio < 0.7; final fitMode = shouldFillScreen ? BoxFit.cover : BoxFit.contain; + final thumbnailOverlay = widget.thumbnail.isNotEmpty + ? Positioned.fill( + child: IgnorePointer( + child: Image.network( + widget.thumbnail, + fit: BoxFit.contain, + width: double.infinity, + height: double.infinity, + ), + ), + ) + : const Positioned.fill( + child: IgnorePointer( + child: DecoratedBox( + decoration: BoxDecoration(color: AppColors.black), + ), + ), + ); + return Stack( alignment: Alignment.center, children: [ @@ -336,6 +405,7 @@ class PostVideoPlayerState extends ConsumerState ) : BetterPlayer(controller: videoController!), ), + if (_showThumbnailOverlay) thumbnailOverlay, Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.opaque, -- 2.51.2