This commit is contained in:
2025-06-24 13:05:08 +02:00
parent ba7505c2b3
commit 93a89ba4b9
3 changed files with 67 additions and 67 deletions

View File

@ -282,17 +282,24 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
Widget _buildMedia(MediaItem item, bool isActive) {
Widget mediaWidget;
final bool isFavorite =
item.favorites?.any((f) => f.userId == authController.user.value?.id) ??
false;
if (item.mime.startsWith('image/')) {
mediaWidget = CachedNetworkImage(
imageUrl: item.mediaUrl,
fit: BoxFit.contain,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (c, e, s) => const Icon(Icons.broken_image, size: 100),
mediaWidget = GestureDetector(
onDoubleTap: () => _handleFavoriteToggle(item, isFavorite),
child: CachedNetworkImage(
imageUrl: item.mediaUrl,
fit: BoxFit.contain,
placeholder: (context, url) =>
const Center(child: CircularProgressIndicator()),
errorWidget: (c, e, s) => const Icon(Icons.broken_image, size: 100),
),
);
} else if (item.mime.startsWith('video/') ||
item.mime.startsWith('audio/')) {
final key = _videoWidgetKeys.putIfAbsent(
final GlobalKey<VideoWidgetState> key = _videoWidgetKeys.putIfAbsent(
item.id,
() => GlobalKey<VideoWidgetState>(),
);
@ -305,39 +312,33 @@ class _MediaDetailScreenState extends State<MediaDetailScreen> {
setState(() => _readyItemIds.add(item.id));
}
},
onDoubleTap: () => _handleFavoriteToggle(item, isFavorite),
);
} else {
mediaWidget = const Icon(Icons.help_outline, size: 100);
}
final bool isFavorite =
item.favorites?.any((f) => f.userId == authController.user.value?.id) ??
false;
return Hero(
tag: 'media_${item.id}',
child: GestureDetector(
onDoubleTap: () => _handleFavoriteToggle(item, isFavorite),
child: Stack(
alignment: Alignment.center,
children: [
mediaWidget,
AnimatedOpacity(
opacity: _showFavoriteAnimation[item.id] ?? false ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: AnimatedScale(
scale: _showFavoriteAnimation[item.id] ?? false ? 1.0 : 0.5,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOutBack,
child: Icon(
isFavorite ? Icons.favorite : Icons.favorite_outline,
color: Colors.red,
size: 100,
),
child: Stack(
alignment: Alignment.center,
children: [
mediaWidget,
AnimatedOpacity(
opacity: _showFavoriteAnimation[item.id] ?? false ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: AnimatedScale(
scale: _showFavoriteAnimation[item.id] ?? false ? 1.0 : 0.5,
duration: const Duration(milliseconds: 400),
curve: Curves.easeOutBack,
child: Icon(
isFavorite ? Icons.favorite : Icons.favorite_outline,
color: Colors.red,
size: 100,
),
),
],
),
),
],
),
);
}

View File

@ -6,8 +6,13 @@ import 'package:f0ckapp/controller/settingscontroller.dart';
class VideoControlsOverlay extends StatefulWidget {
final CachedVideoPlayerPlusController controller;
final VoidCallback? onDoubleTap;
const VideoControlsOverlay({super.key, required this.controller});
const VideoControlsOverlay({
super.key,
required this.controller,
this.onDoubleTap,
});
@override
State<VideoControlsOverlay> createState() => _VideoControlsOverlayState();
@ -140,27 +145,9 @@ class _VideoControlsOverlayState extends State<VideoControlsOverlay> {
AnimatedOpacity(
opacity: _controlsVisible && !_isScrubbing ? 1.0 : 0.0,
duration: const Duration(milliseconds: 300),
child: IgnorePointer(
ignoring: !_controlsVisible,
child: Stack(
alignment: Alignment.center,
children: [
IconButton(
icon: Icon(
widget.controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
size: 64,
color: Theme.of(context).colorScheme.primary,
),
onPressed: _handlePlayPause,
),
Align(
alignment: Alignment.bottomCenter,
child: _buildBottomBar(),
),
],
),
child: Align(
alignment: Alignment.bottomCenter,
child: _buildBottomBar(),
),
),
],
@ -208,6 +195,18 @@ class _VideoControlsOverlayState extends State<VideoControlsOverlay> {
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Row(
children: [
IconButton(
icon: Icon(
widget.controller.value.isPlaying
? Icons.pause
: Icons.play_arrow,
color: Theme.of(context).colorScheme.primary,
size: 20,
),
onPressed: _handlePlayPause,
constraints: const BoxConstraints(),
padding: EdgeInsets.zero,
),
Text(
_formatDuration(widget.controller.value.position),
style: TextStyle(

View File

@ -17,6 +17,7 @@ class VideoWidget extends StatefulWidget {
final bool fullScreen;
final VoidCallback? onInitialized;
final Duration? initialPosition;
final VoidCallback? onDoubleTap;
const VideoWidget({
super.key,
@ -25,6 +26,7 @@ class VideoWidget extends StatefulWidget {
this.fullScreen = false,
this.onInitialized,
this.initialPosition,
this.onDoubleTap,
});
@override
@ -56,6 +58,7 @@ class VideoWidgetState extends State<VideoWidget> {
await videoController.initialize();
if (!mounted) return;
// Rebuild the widget to reflect the initialized state
setState(() {});
if (widget.initialPosition != null) {
@ -100,7 +103,8 @@ class VideoWidgetState extends State<VideoWidget> {
@override
Widget build(BuildContext context) {
bool isAudio = widget.details.mime.startsWith('audio');
final bool isInitialized = videoController.value.isInitialized;
final bool isAudio = widget.details.mime.startsWith('audio');
Widget mediaContent;
if (isAudio) {
@ -114,30 +118,26 @@ class VideoWidgetState extends State<VideoWidget> {
),
);
} else {
mediaContent = videoController.value.isInitialized
mediaContent = isInitialized
? CachedVideoPlayerPlus(videoController)
: const Center(child: CircularProgressIndicator());
}
return AspectRatio(
aspectRatio: videoController.value.isInitialized
aspectRatio: isInitialized
? videoController.value.aspectRatio
: (isAudio ? 16 / 9 : 9 / 16),
child: Stack(
alignment: Alignment.center,
children: [
mediaContent,
AnimatedBuilder(
animation: videoController,
builder: (context, child) {
if (videoController.value.isInitialized) {
return Positioned.fill(
child: VideoControlsOverlay(controller: videoController),
);
}
return const SizedBox.shrink();
},
),
if (isInitialized)
Positioned.fill(
child: VideoControlsOverlay(
controller: videoController,
onDoubleTap: widget.onDoubleTap,
),
),
],
),
);