feat(video): optimize video player aspect ratio sizing and seekbar positioning v3.1.4
Build and Release / Build Windows x64 (push) Has been cancelled

This commit is contained in:
2026-09-04 21:50:17 +03:00
parent 254f0e3d59
commit 9a0fd282b7
6 changed files with 166 additions and 64 deletions
@@ -219,34 +219,71 @@ class PostDetailsScreen extends ConsumerWidget {
),
Expanded(
child: Center(
child: SizedBox(
width: double.infinity,
height: 760,
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders: ref
.watch(postMediaHeadersProvider(post))
.value ??
const {},
initialPosition: Duration(
milliseconds: settings.videoPlaybackPositions[
post.cacheKey] ??
0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume:
settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
),
),
child: MediaUrlSelector.isVideo(post)
? ConstrainedBox(
constraints: const BoxConstraints(
maxHeight: 760,
),
child: AspectRatio(
aspectRatio:
(post.width > 0 && post.height > 0)
? (post.width / post.height)
.clamp(0.45, 2.4)
: (16 / 9),
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders: ref
.watch(postMediaHeadersProvider(post))
.value ??
const {},
initialPosition: Duration(
milliseconds: settings.videoPlaybackPositions[
post.cacheKey] ??
0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume:
settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
),
),
)
: SizedBox(
width: double.infinity,
height: 760,
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders: ref
.watch(postMediaHeadersProvider(post))
.value ??
const {},
initialPosition: Duration(
milliseconds: settings.videoPlaybackPositions[
post.cacheKey] ??
0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume:
settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
),
),
),
),
IconButton.filledTonal(
@@ -409,33 +446,72 @@ class PostDetailsScreen extends ConsumerWidget {
? null
: () =>
_showMobileQuickActions(context, ref, post, favoriteKeys),
child: SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.62,
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders:
ref.watch(postMediaHeadersProvider(post)).value ?? const {},
initialPosition: Duration(
milliseconds:
settings.videoPlaybackPositions[post.cacheKey] ?? 0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume: settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
onMediaGestureLockChanged: (locked) {
onMediaGestureLockChanged?.call(locked);
},
),
),
child: isVideo
? Center(
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.sizeOf(context).height * 0.70,
),
child: AspectRatio(
aspectRatio: (post.width > 0 && post.height > 0)
? (post.width / post.height).clamp(0.45, 2.4)
: (16 / 9),
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders:
ref.watch(postMediaHeadersProvider(post)).value ??
const {},
initialPosition: Duration(
milliseconds:
settings.videoPlaybackPositions[post.cacheKey] ??
0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume: settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
onMediaGestureLockChanged: (locked) {
onMediaGestureLockChanged?.call(locked);
},
),
),
),
)
: SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height * 0.62,
child: PostMediaViewer(
key: ValueKey(post.cacheKey),
post: post,
localFilePath: localMedia?.savedPath,
qualityMode: qualityMode,
mediaHeaders:
ref.watch(postMediaHeadersProvider(post)).value ??
const {},
initialPosition: Duration(
milliseconds:
settings.videoPlaybackPositions[post.cacheKey] ?? 0,
),
initialLoop: settings.videoPlayerLoop,
initialMuted: settings.videoPlayerMuted,
initialCoverVideo: settings.videoPlayerCover,
initialHalfVolume: settings.videoPlayerHalfVolume,
onPlaybackSnapshot: (snapshot) =>
_saveVideoSnapshot(ref, post, snapshot),
onPlaybackPreferencesChanged: (snapshot) =>
_saveVideoPreferences(ref, snapshot),
onMediaGestureLockChanged: (locked) {
onMediaGestureLockChanged?.call(locked);
},
),
),
),
const SizedBox(height: 12),
PostActionBar(
@@ -1792,7 +1792,10 @@ class _VideoControls extends StatelessWidget {
),
),
child: SafeArea(
top: fullscreen,
bottom: false,
left: fullscreen,
right: fullscreen,
child: Row(
children: [
if (fullscreen) ...[
@@ -1910,7 +1913,12 @@ class _VideoControls extends StatelessWidget {
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.fromLTRB(14, 12, 14, 10),
padding: EdgeInsets.fromLTRB(
14,
8,
14,
fullscreen ? 10 : 4,
),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
@@ -1920,6 +1928,9 @@ class _VideoControls extends StatelessWidget {
),
child: SafeArea(
top: false,
bottom: fullscreen,
left: fullscreen,
right: fullscreen,
child: StreamBuilder<Duration>(
stream: player.stream.duration,
initialData: player.state.duration,
@@ -1967,8 +1978,8 @@ class _VideoControls extends StatelessWidget {
// Buffer progress track
if (maxMs > 0 && bufferMs > 0)
Positioned(
left: 20,
right: 20,
left: 14,
right: 14,
child: ClipRRect(
borderRadius:
BorderRadius.circular(2),
@@ -1976,10 +1987,12 @@ class _VideoControls extends StatelessWidget {
value: (bufferMs / maxMs)
.clamp(0.0, 1.0),
minHeight: 3.5,
backgroundColor: Colors.transparent,
backgroundColor:
Colors.transparent,
valueColor:
AlwaysStoppedAnimation(
Colors.white.withValues(alpha: 0.28),
Colors.white
.withValues(alpha: 0.28),
),
),
),
@@ -1997,6 +2010,10 @@ class _VideoControls extends StatelessWidget {
const RoundSliderThumbShape(
enabledThumbRadius: 6,
),
overlayShape:
const RoundSliderOverlayShape(
overlayRadius: 14,
),
),
child: Slider(
value: valueMs,