diff --git a/mobile/lib/constants/immich_colors.dart b/mobile/lib/constants/immich_colors.dart index 37e98a7f70..d63928b5b8 100644 --- a/mobile/lib/constants/immich_colors.dart +++ b/mobile/lib/constants/immich_colors.dart @@ -20,7 +20,7 @@ const String defaultColorPresetName = "indigo"; const Color immichBrandColorLight = Color(0xFF4150AF); const Color immichBrandColorDark = Color(0xFFACCBFA); const Color whiteOpacity75 = Color.fromARGB((0.75 * 255) ~/ 1, 255, 255, 255); -const Color blackOpacity40 = Color.fromARGB((0.40 * 255) ~/ 1, 0, 0, 0); +const Color blackOpacity90 = Color.fromARGB((0.90 * 255) ~/ 1, 0, 0, 0); final Map _themePresetsMap = { ImmichColorPreset.indigo: ImmichTheme( diff --git a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart index f550857b9d..eadaf0bf9f 100644 --- a/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart +++ b/mobile/lib/widgets/asset_viewer/bottom_gallery_bar.dart @@ -5,6 +5,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/constants/immich_colors.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/album/album.provider.dart'; import 'package:immich_mobile/providers/album/current_album.provider.dart'; @@ -327,39 +328,51 @@ class BottomGalleryBar extends ConsumerWidget { child: AnimatedOpacity( duration: const Duration(milliseconds: 100), opacity: ref.watch(showControlsProvider) ? 1.0 : 0.0, - child: Column( - children: [ - Visibility( - visible: showVideoPlayerControls, - child: const VideoControls(), + child: DecoratedBox( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [blackOpacity90, Colors.transparent], ), - BottomNavigationBar( - backgroundColor: Colors.black.withOpacity(0.4), - unselectedIconTheme: const IconThemeData(color: Colors.white), - selectedIconTheme: const IconThemeData(color: Colors.white), - unselectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - selectedLabelStyle: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.w500, - height: 2.3, - ), - unselectedFontSize: 14, - selectedFontSize: 14, - selectedItemColor: Colors.white, - unselectedItemColor: Colors.white, - showSelectedLabels: true, - showUnselectedLabels: true, - items: - albumActions.map((e) => e.keys.first).toList(growable: false), - onTap: (index) { - albumActions[index].values.first.call(index); - }, + ), + position: DecorationPosition.background, + child: Padding( + padding: EdgeInsets.only(top: 40.0), + child: Column( + children: [ + if (showVideoPlayerControls) const VideoControls(), + BottomNavigationBar( + elevation: 0.0, + backgroundColor: Colors.transparent, + unselectedIconTheme: const IconThemeData(color: Colors.white), + selectedIconTheme: const IconThemeData(color: Colors.white), + unselectedLabelStyle: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + height: 2.3, + ), + selectedLabelStyle: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w500, + height: 2.3, + ), + unselectedFontSize: 14, + selectedFontSize: 14, + selectedItemColor: Colors.white, + unselectedItemColor: Colors.white, + showSelectedLabels: true, + showUnselectedLabels: true, + items: albumActions + .map((e) => e.keys.first) + .toList(growable: false), + onTap: (index) { + albumActions[index].values.first.call(index); + }, + ), + ], ), - ], + ), ), ), ); diff --git a/mobile/lib/widgets/asset_viewer/formatted_duration.dart b/mobile/lib/widgets/asset_viewer/formatted_duration.dart index 4a334cd7cc..a34aab7d12 100644 --- a/mobile/lib/widgets/asset_viewer/formatted_duration.dart +++ b/mobile/lib/widgets/asset_viewer/formatted_duration.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:immich_mobile/constants/immich_colors.dart'; @pragma('vm:prefer-inline') String _formatDuration(Duration position) { @@ -24,8 +23,8 @@ class FormattedDuration extends StatelessWidget { _formatDuration(data), style: const TextStyle( fontSize: 14.0, - color: whiteOpacity75, - fontWeight: FontWeight.normal, + color: Colors.white, + fontWeight: FontWeight.w500, ), textAlign: TextAlign.center, ), diff --git a/mobile/lib/widgets/asset_viewer/video_controls.dart b/mobile/lib/widgets/asset_viewer/video_controls.dart index c96d58d374..e4d78324c8 100644 --- a/mobile/lib/widgets/asset_viewer/video_controls.dart +++ b/mobile/lib/widgets/asset_viewer/video_controls.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/constants/immich_colors.dart'; -import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/video_position.dart'; /// The video controls for the [videoPlayerControlsProvider] @@ -12,24 +10,11 @@ class VideoControls extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait; - return AnimatedOpacity( - opacity: ref.watch(showControlsProvider) ? 1.0 : 0.0, - duration: const Duration(milliseconds: 100), - child: isPortrait - ? const ColoredBox( - color: blackOpacity40, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 24.0), - child: VideoPosition(), - ), - ) - : const ColoredBox( - color: blackOpacity40, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 128.0), - child: VideoPosition(), - ), - ), - ); + return isPortrait + ? const VideoPosition() + : const Padding( + padding: EdgeInsets.symmetric(horizontal: 60.0), + child: VideoPosition(), + ); } } diff --git a/mobile/lib/widgets/asset_viewer/video_mute_button.dart b/mobile/lib/widgets/asset_viewer/video_mute_button.dart deleted file mode 100644 index da0f6f3174..0000000000 --- a/mobile/lib/widgets/asset_viewer/video_mute_button.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; - -class VideoMuteButton extends ConsumerWidget { - const VideoMuteButton({super.key}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return IconButton( - icon: ref.watch( - videoPlayerControlsProvider.select((value) => value.mute), - ) - ? const Icon(Icons.volume_off) - : const Icon(Icons.volume_up), - onPressed: () => - ref.read(videoPlayerControlsProvider.notifier).toggleMute(), - color: Colors.white, - padding: const EdgeInsets.all(0), - alignment: Alignment.centerRight, - ); - } -} diff --git a/mobile/lib/widgets/asset_viewer/video_position.dart b/mobile/lib/widgets/asset_viewer/video_position.dart index 0512785782..ef309b9c85 100644 --- a/mobile/lib/widgets/asset_viewer/video_position.dart +++ b/mobile/lib/widgets/asset_viewer/video_position.dart @@ -7,7 +7,6 @@ import 'package:immich_mobile/constants/immich_colors.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_controls_provider.dart'; import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart'; import 'package:immich_mobile/widgets/asset_viewer/formatted_duration.dart'; -import 'package:immich_mobile/widgets/asset_viewer/video_mute_button.dart'; class VideoPosition extends HookConsumerWidget { const VideoPosition({super.key}); @@ -20,38 +19,52 @@ class VideoPosition extends HookConsumerWidget { final wasPlaying = useRef(true); return duration == Duration.zero ? const _VideoPositionPlaceholder() - : Row( + : Column( children: [ - FormattedDuration(position), - Expanded( - child: Slider( - value: min( - position.inMicroseconds / duration.inMicroseconds * 100, - 100, - ), - min: 0, - max: 100, - thumbColor: Colors.white, - activeColor: Colors.white, - inactiveColor: whiteOpacity75, - onChangeStart: (value) { - final state = ref.read(videoPlaybackValueProvider).state; - wasPlaying.value = state != VideoPlaybackState.paused; - ref.read(videoPlayerControlsProvider.notifier).pause(); - }, - onChangeEnd: (value) { - if (wasPlaying.value) { - ref.read(videoPlayerControlsProvider.notifier).play(); - } - }, - onChanged: (position) { - ref.read(videoPlayerControlsProvider.notifier).position = - position; - }, + Padding( + // align with slider's inherent padding + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FormattedDuration(position), + FormattedDuration(duration), + ], ), ), - FormattedDuration(duration), - const VideoMuteButton(), + Row( + children: [ + Expanded( + child: Slider( + value: min( + position.inMicroseconds / duration.inMicroseconds * 100, + 100, + ), + min: 0, + max: 100, + thumbColor: Colors.white, + activeColor: Colors.white, + inactiveColor: whiteOpacity75, + onChangeStart: (value) { + final state = + ref.read(videoPlaybackValueProvider).state; + wasPlaying.value = state != VideoPlaybackState.paused; + ref.read(videoPlayerControlsProvider.notifier).pause(); + }, + onChangeEnd: (value) { + if (wasPlaying.value) { + ref.read(videoPlayerControlsProvider.notifier).play(); + } + }, + onChanged: (position) { + ref + .read(videoPlayerControlsProvider.notifier) + .position = position; + }, + ), + ), + ], + ), ], ); } @@ -64,22 +77,33 @@ class _VideoPositionPlaceholder extends StatelessWidget { @override Widget build(BuildContext context) { - return const Row( + return const Column( children: [ - FormattedDuration(Duration.zero), - Expanded( - child: Slider( - value: 0.0, - min: 0, - max: 100, - thumbColor: Colors.white, - activeColor: Colors.white, - inactiveColor: whiteOpacity75, - onChanged: _onChangedDummy, + Padding( + padding: EdgeInsets.symmetric(horizontal: 12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FormattedDuration(Duration.zero), + FormattedDuration(Duration.zero), + ], ), ), - FormattedDuration(Duration.zero), - VideoMuteButton(), + Row( + children: [ + Expanded( + child: Slider( + value: 0.0, + min: 0, + max: 100, + thumbColor: Colors.white, + activeColor: Colors.white, + inactiveColor: whiteOpacity75, + onChanged: _onChangedDummy, + ), + ), + ], + ), ], ); }