From ff3cde4dfb7703bda8960a5690006da5e3c4f722 Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Fri, 3 Feb 2023 11:26:05 -0500 Subject: [PATCH] feat(mobile): Fullscreen image effects (#1529) * fullscreen image effects * toggles app bar instead of hides on tap * edgeToEdge mode to render beneath navbar on android * fixed appbar size * fixed safearea for video and added opacity to appbar in gallery * wrapped in black container to fix artifact on iOS * changed to black * added scaffold back woops --- mobile/lib/main.dart | 3 + .../asset_viewer/ui/top_control_app_bar.dart | 6 +- .../asset_viewer/views/gallery_viewer.dart | 100 +++++++++++------- 3 files changed, 68 insertions(+), 41 deletions(-) diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 6564b20139..262612e44d 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -155,6 +156,8 @@ class ImmichAppState extends ConsumerState var router = ref.watch(appRouterProvider); ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo(); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + return MaterialApp( localizationsDelegates: context.localizationDelegates, supportedLocales: context.supportedLocales, diff --git a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart index a5c80dde89..815f5b175d 100644 --- a/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart +++ b/mobile/lib/modules/asset_viewer/ui/top_control_app_bar.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/shared/models/asset.dart'; -class TopControlAppBar extends HookConsumerWidget with PreferredSizeWidget { +class TopControlAppBar extends HookConsumerWidget { const TopControlAppBar({ Key? key, required this.asset, @@ -31,7 +31,6 @@ class TopControlAppBar extends HookConsumerWidget with PreferredSizeWidget { return AppBar( foregroundColor: Colors.grey[100], - toolbarHeight: 60, backgroundColor: Colors.transparent, leading: IconButton( onPressed: () { @@ -120,7 +119,4 @@ class TopControlAppBar extends HookConsumerWidget with PreferredSizeWidget { ], ); } - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); } diff --git a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart index 19db5292bc..78d70b35fd 100644 --- a/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart +++ b/mobile/lib/modules/asset_viewer/views/gallery_viewer.dart @@ -48,6 +48,7 @@ class GalleryViewerPage extends HookConsumerWidget { final isLoadPreview = useState(AppSettingsEnum.loadPreview.defaultValue); final isLoadOriginal = useState(AppSettingsEnum.loadOriginal.defaultValue); final isZoomed = useState(false); + final showAppBar = useState(true); final indexOfAsset = useState(assetList.indexOf(asset)); final isPlayingMotionVideo = useState(false); late Offset localPosition; @@ -227,36 +228,50 @@ class GalleryViewerPage extends HookConsumerWidget { } } + buildAppBar() { + return AnimatedOpacity( + duration: const Duration(milliseconds: 100), + opacity: (showAppBar.value || !isZoomed.value) ? 1.0 : 0.0, + child: Container( + color: Colors.black.withOpacity(0.4), + child: TopControlAppBar( + isPlayingMotionVideo: isPlayingMotionVideo.value, + asset: assetList[indexOfAsset.value], + onMoreInfoPressed: () { + showInfo(); + }, + onDownloadPressed: assetList[indexOfAsset.value].isLocal + ? null + : () { + ref.watch(imageViewerStateProvider.notifier).downloadAsset( + assetList[indexOfAsset.value].remote!, + context, + ); + }, + onSharePressed: () { + ref + .watch(imageViewerStateProvider.notifier) + .shareAsset(assetList[indexOfAsset.value], context); + }, + onToggleMotionVideo: (() { + isPlayingMotionVideo.value = !isPlayingMotionVideo.value; + }), + onDeletePressed: () => handleDelete((assetList[indexOfAsset.value])), + onAddToAlbumPressed: () => addToAlbum(assetList[indexOfAsset.value]), + ), + ), + ); + } + return Scaffold( backgroundColor: Colors.black, - appBar: TopControlAppBar( - isPlayingMotionVideo: isPlayingMotionVideo.value, - asset: assetList[indexOfAsset.value], - onMoreInfoPressed: () { - showInfo(); - }, - onDownloadPressed: assetList[indexOfAsset.value].isLocal - ? null - : () { - ref.watch(imageViewerStateProvider.notifier).downloadAsset( - assetList[indexOfAsset.value].remote!, - context, - ); - }, - onSharePressed: () { - ref - .watch(imageViewerStateProvider.notifier) - .shareAsset(assetList[indexOfAsset.value], context); - }, - onToggleMotionVideo: (() { - isPlayingMotionVideo.value = !isPlayingMotionVideo.value; - }), - onDeletePressed: () => handleDelete((assetList[indexOfAsset.value])), - onAddToAlbumPressed: () => addToAlbum(assetList[indexOfAsset.value]), - ), - body: SafeArea( - child: PhotoViewGallery.builder( - scaleStateChangedCallback: (state) => isZoomed.value = state != PhotoViewScaleState.initial, + body: Stack( + children: [ + PhotoViewGallery.builder( + scaleStateChangedCallback: (state) { + isZoomed.value = state != PhotoViewScaleState.initial; + showAppBar.value = !isZoomed.value; + }, pageController: controller, scrollPhysics: isZoomed.value ? const NeverScrollableScrollPhysics() // Don't allow paging while scrolled in @@ -327,6 +342,7 @@ class GalleryViewerPage extends HookConsumerWidget { return PhotoViewGalleryPageOptions( onDragStart: (_, details, __) => localPosition = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), + onTapDown: (_, __, ___) => showAppBar.value = !showAppBar.value, imageProvider: provider, heroAttributes: PhotoViewHeroAttributes(tag: assetList[index].id), minScale: PhotoViewComputedScale.contained, @@ -335,20 +351,32 @@ class GalleryViewerPage extends HookConsumerWidget { return PhotoViewGalleryPageOptions.customChild( onDragStart: (_, details, __) => localPosition = details.localPosition, onDragUpdate: (_, details, __) => handleSwipeUpDown(details), + onTapDown: (_, __, ___) => showAppBar.value = !showAppBar.value, heroAttributes: PhotoViewHeroAttributes(tag: assetList[index].id), - child: VideoViewerPage( - asset: assetList[index], - isMotionVideo: isPlayingMotionVideo.value, - onVideoEnded: () { - if (isPlayingMotionVideo.value) { - isPlayingMotionVideo.value = false; - } - }, + maxScale: 1.0, + minScale: 1.0, + child: SafeArea( + child: VideoViewerPage( + asset: assetList[index], + isMotionVideo: isPlayingMotionVideo.value, + onVideoEnded: () { + if (isPlayingMotionVideo.value) { + isPlayingMotionVideo.value = false; + } + }, + ), ), ); } }, ), + Positioned( + top: 0, + left: 0, + right: 0, + child: buildAppBar(), + ), + ], ), ); }