From ddc1dc7daf072e99dcecd375681a6cbb286daf4a Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 19 Jan 2025 01:00:00 +0800 Subject: [PATCH] :lipstick: Optimize attachment zoom page --- android/app/src/main/AndroidManifest.xml | 1 + lib/theme.dart | 10 + lib/widgets/attachment/attachment_zoom.dart | 382 ++++++++++---------- 3 files changed, 205 insertions(+), 188 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 77ad16c..803c22d 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ android:label="Solian" android:name="${applicationName}" android:icon="@mipmap/ic_launcher" + android:enableOnBackInvokedCallback="true" android:requestLegacyExternalStorage="true"> createAppTheme( foregroundColor: hasAppBarBlurry ? colorScheme.onSurface : colorScheme.onPrimary, ), scaffoldBackgroundColor: Colors.transparent, + pageTransitionsTheme: PageTransitionsTheme( + builders: { + TargetPlatform.android: PredictiveBackPageTransitionsBuilder(), + TargetPlatform.iOS: ZoomPageTransitionsBuilder(), + TargetPlatform.macOS: ZoomPageTransitionsBuilder(), + TargetPlatform.fuchsia: ZoomPageTransitionsBuilder(), + TargetPlatform.linux: ZoomPageTransitionsBuilder(), + TargetPlatform.windows: ZoomPageTransitionsBuilder(), + }, + ), ); } diff --git a/lib/widgets/attachment/attachment_zoom.dart b/lib/widgets/attachment/attachment_zoom.dart index 3bbe9bf..0382e88 100644 --- a/lib/widgets/attachment/attachment_zoom.dart +++ b/lib/widgets/attachment/attachment_zoom.dart @@ -144,218 +144,224 @@ class _AttachmentZoomViewState extends State { onDismissed: () { Navigator.of(context).pop(); }, - direction: DismissiblePageDismissDirection.down, + direction: DismissiblePageDismissDirection.none, backgroundColor: Colors.transparent, isFullScreen: true, - child: Scaffold( - body: Stack( - children: [ - Builder(builder: (context) { - if (widget.data.length == 1) { - final heroTag = widget.heroTags?.first ?? uuid.v4(); - return Hero( - tag: 'attachment-${widget.data.first.rid}-$heroTag', - child: PhotoView( - key: Key('attachment-detail-${widget.data.first.rid}-$heroTag'), - backgroundDecoration: BoxDecoration(color: Colors.transparent), - imageProvider: UniversalImage.provider( - sn.getAttachmentUrl(widget.data.first.rid), - ), - ), - ); - } - - return PhotoViewGallery.builder( - pageController: _pageController, - scrollPhysics: const BouncingScrollPhysics(), - builder: (context, idx) { - final heroTag = widget.heroTags?.elementAt(idx) ?? uuid.v4(); - return PhotoViewGalleryPageOptions( - imageProvider: UniversalImage.provider( - sn.getAttachmentUrl(widget.data.elementAt(idx).rid), - ), - heroAttributes: PhotoViewHeroAttributes( - tag: 'attachment-${widget.data.first.rid}-$heroTag', + child: GestureDetector( + behavior: HitTestBehavior.translucent, + child: Scaffold( + body: Stack( + children: [ + Builder(builder: (context) { + if (widget.data.length == 1) { + final heroTag = widget.heroTags?.first ?? uuid.v4(); + return Hero( + tag: 'attachment-${widget.data.first.rid}-$heroTag', + child: PhotoView( + key: Key('attachment-detail-${widget.data.first.rid}-$heroTag'), + backgroundDecoration: BoxDecoration(color: Colors.transparent), + imageProvider: UniversalImage.provider( + sn.getAttachmentUrl(widget.data.first.rid), + ), ), ); - }, - itemCount: widget.data.length, - loadingBuilder: (context, event) => Center( - child: SizedBox( - width: 20.0, - height: 20.0, - child: CircularProgressIndicator( - value: event == null ? 0 : event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 1), + } + + return PhotoViewGallery.builder( + pageController: _pageController, + scrollPhysics: const BouncingScrollPhysics(), + builder: (context, idx) { + final heroTag = widget.heroTags?.elementAt(idx) ?? uuid.v4(); + return PhotoViewGalleryPageOptions( + imageProvider: UniversalImage.provider( + sn.getAttachmentUrl(widget.data.elementAt(idx).rid), + ), + heroAttributes: PhotoViewHeroAttributes( + tag: 'attachment-${widget.data.first.rid}-$heroTag', + ), + ); + }, + itemCount: widget.data.length, + loadingBuilder: (context, event) => Center( + child: SizedBox( + width: 20.0, + height: 20.0, + child: CircularProgressIndicator( + value: event == null ? 0 : event.cumulativeBytesLoaded / (event.expectedTotalBytes ?? 1), + ), ), ), - ), - backgroundDecoration: BoxDecoration(color: Colors.transparent), - ); - }), - Align( - alignment: Alignment.bottomCenter, - child: IgnorePointer( - child: Container( - height: 300, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.bottomCenter, - end: Alignment.topCenter, - colors: [ - Theme.of(context).colorScheme.surface, - Colors.transparent, - ], + backgroundDecoration: BoxDecoration(color: Colors.transparent), + ); + }), + Align( + alignment: Alignment.bottomCenter, + child: IgnorePointer( + child: Container( + height: 300, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surface, + Colors.transparent, + ], + ), ), ), ), ), - ), - Positioned( - left: 16, - right: 16, - bottom: 16 + MediaQuery.of(context).padding.bottom, - child: Material( - color: Colors.transparent, - child: Builder(builder: (context) { - final ud = context.read(); - final item = widget.data.elementAt( - widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0, - ); - final account = ud.getAccountFromCache(item.accountId); + Positioned( + left: 16, + right: 16, + bottom: 16 + MediaQuery.of(context).padding.bottom, + child: Material( + color: Colors.transparent, + child: Builder(builder: (context) { + final ud = context.read(); + final item = widget.data.elementAt( + widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0, + ); + final account = ud.getAccountFromCache(item.accountId); - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (item.accountId > 0) - Row( - children: [ - IgnorePointer( - child: AccountImage( - content: account?.avatar, - radius: 19, - ), - ), - const Gap(8), - Expanded( - child: IgnorePointer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'attachmentUploadBy'.tr(), - style: Theme.of(context).textTheme.bodySmall, - ), - Text( - account?.nick ?? 'unknown'.tr(), - style: Theme.of(context).textTheme.bodyMedium, - ), - ], + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (item.accountId > 0) + Row( + children: [ + IgnorePointer( + child: AccountImage( + content: account?.avatar, + radius: 19, ), ), - ), - if (widget.data.length > 1) - IgnorePointer( - child: Text( - '${(_pageController.page?.round() ?? 0) + 1}/${widget.data.length}', - style: GoogleFonts.robotoMono(fontSize: 13), - ).padding(right: 8), - ), - InkWell( - borderRadius: const BorderRadius.all(Radius.circular(16)), - onTap: _isDownloading - ? null - : () => _saveToAlbum(widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0), - child: Container( - padding: const EdgeInsets.all(6), - child: !_isDownloading - ? !_isCompletedDownload - ? const Icon(Symbols.save_alt) - : const Icon(Symbols.download_done) - : SizedBox( - width: 24, - height: 24, - child: CircularProgressIndicator( - value: _progressOfDownload, - strokeWidth: 3, - ), + const Gap(8), + Expanded( + child: IgnorePointer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'attachmentUploadBy'.tr(), + style: Theme.of(context).textTheme.bodySmall, ), + Text( + account?.nick ?? 'unknown'.tr(), + style: Theme.of(context).textTheme.bodyMedium, + ), + ], + ), + ), ), + if (widget.data.length > 1) + IgnorePointer( + child: Text( + '${(_pageController.page?.round() ?? 0) + 1}/${widget.data.length}', + style: GoogleFonts.robotoMono(fontSize: 13), + ).padding(right: 8), + ), + InkWell( + borderRadius: const BorderRadius.all(Radius.circular(16)), + onTap: _isDownloading + ? null + : () => _saveToAlbum(widget.data.length > 1 ? _pageController.page?.round() ?? 0 : 0), + child: Container( + padding: const EdgeInsets.all(6), + child: !_isDownloading + ? !_isCompletedDownload + ? const Icon(Symbols.save_alt) + : const Icon(Symbols.download_done) + : SizedBox( + width: 24, + height: 24, + child: CircularProgressIndicator( + value: _progressOfDownload, + strokeWidth: 3, + ), + ), + ), + ), + ], + ), + const Gap(4), + IgnorePointer( + child: Text( + item.alt, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.w500, ), - ], - ), - const Gap(4), - IgnorePointer( - child: Text( - item.alt, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.w500, ), ), - ), - const Gap(2), - IgnorePointer( - child: Wrap( - spacing: 6, - children: [ - if (item.metadata['exif'] == null) + const Gap(2), + IgnorePointer( + child: Wrap( + spacing: 6, + children: [ + if (item.metadata['exif'] == null) + Text( + '#${item.rid}', + style: metaTextStyle, + ), + if (item.metadata['exif']?['Model'] != null) + Text( + 'attachmentShotOn'.tr(args: [ + item.metadata['exif']?['Model'], + ]), + style: metaTextStyle, + ).padding(right: 2), + if (item.metadata['exif']?['ISO'] != null) + Text( + 'ISO${item.metadata['exif']?['ISO']}', + style: metaTextStyle, + ).padding(right: 2), + if (item.metadata['exif']?['Aperture'] != null) + Text( + 'f/${item.metadata['exif']?['Aperture']}', + style: metaTextStyle, + ).padding(right: 2), + if (item.metadata['exif']?['Megapixels'] != null && item.metadata['exif']?['Model'] != null) + Text( + '${item.metadata['exif']?['Megapixels']}MP', + style: metaTextStyle, + ) + else + Text( + item.size.formatBytes(), + style: metaTextStyle, + ), + if (item.metadata['width'] != null && item.metadata['height'] != null) + Text( + '${item.metadata['width']}x${item.metadata['height']}', + style: metaTextStyle, + ), + if (item.metadata['ratio'] != null) + Text( + (item.metadata['ratio'] as num).toStringAsFixed(2), + style: metaTextStyle, + ), Text( - '#${item.rid}', + item.mimetype, style: metaTextStyle, ), - if (item.metadata['exif']?['Model'] != null) - Text( - 'attachmentShotOn'.tr(args: [ - item.metadata['exif']?['Model'], - ]), - style: metaTextStyle, - ).padding(right: 2), - if (item.metadata['exif']?['ISO'] != null) - Text( - 'ISO${item.metadata['exif']?['ISO']}', - style: metaTextStyle, - ).padding(right: 2), - if (item.metadata['exif']?['Aperture'] != null) - Text( - 'f/${item.metadata['exif']?['Aperture']}', - style: metaTextStyle, - ).padding(right: 2), - if (item.metadata['exif']?['Megapixels'] != null && item.metadata['exif']?['Model'] != null) - Text( - '${item.metadata['exif']?['Megapixels']}MP', - style: metaTextStyle, - ) - else - Text( - item.size.formatBytes(), - style: metaTextStyle, - ), - if (item.metadata['width'] != null && item.metadata['height'] != null) - Text( - '${item.metadata['width']}x${item.metadata['height']}', - style: metaTextStyle, - ), - if (item.metadata['ratio'] != null) - Text( - (item.metadata['ratio'] as num).toStringAsFixed(2), - style: metaTextStyle, - ), - Text( - item.mimetype, - style: metaTextStyle, - ), - ], + ], + ), ), - ), - ], - ); - }), + ], + ); + }), + ), ), - ), - ], + ], + ), ), + onTap: () { + Navigator.of(context).pop(); + }, ), ); }