💄 Redesigned attachment zoom view

This commit is contained in:
LittleSheep 2025-03-06 22:29:52 +08:00
parent 54c098c274
commit 115cb4adc1

View File

@ -1,5 +1,4 @@
import 'dart:io'; import 'dart:io';
import 'dart:math' show max;
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:dismissible_page/dismissible_page.dart'; import 'package:dismissible_page/dismissible_page.dart';
@ -48,11 +47,14 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
bool _showOverlay = true; bool _showOverlay = true;
bool _dismissable = true; bool _dismissable = true;
int _page = 0;
void _updatePage() { void _updatePage() {
setState(() { setState(() {
if (_isCompletedDownload) { if (_isCompletedDownload) {
setState(() => _isCompletedDownload = false); setState(() => _isCompletedDownload = false);
} }
_page = _pageController.page?.round() ?? 0;
}); });
} }
@ -222,31 +224,11 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
BoxDecoration(color: Colors.transparent), BoxDecoration(color: Colors.transparent),
); );
}), }),
Positioned(
top: max(MediaQuery.of(context).padding.top, 8),
left: 14,
child: IgnorePointer(
ignoring: !_showOverlay,
child: IconButton(
constraints: const BoxConstraints(),
icon: const Icon(Icons.close),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context).colorScheme.surface.withOpacity(0.5),
),
),
onPressed: () {
Navigator.of(context).pop();
},
).opacity(_showOverlay ? 1 : 0, animate: true).animate(
const Duration(milliseconds: 300), Curves.easeInOut),
),
),
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: IgnorePointer( child: IgnorePointer(
child: Container( child: Container(
height: 300, height: 200,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.bottomCenter, begin: Alignment.bottomCenter,
@ -269,153 +251,130 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
child: Material( child: Material(
color: Colors.transparent, color: Colors.transparent,
child: Builder(builder: (context) { child: Builder(builder: (context) {
final ud = context.read<UserDirectoryProvider>(); return Row(
final item = widget.data.elementAt(
widget.data.length > 1
? _pageController.page?.round() ?? 0
: 0,
);
final account = ud.getFromCache(item.accountId);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
if (item.accountId > 0) IconButton(
Row( iconSize: 18,
children: [ constraints: const BoxConstraints(),
IgnorePointer( icon: const Icon(Icons.close),
child: AccountImage( style: ButtonStyle(
content: account?.avatar, backgroundColor: MaterialStateProperty.all(
radius: 19, Theme.of(context)
), .colorScheme
), .surface
const Gap(8), .withOpacity(0.5),
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,
), ),
), ),
onPressed: () {
Navigator.of(context).pop();
},
), ),
const Gap(2), IconButton(
IgnorePointer( iconSize: 20,
child: Wrap( constraints: const BoxConstraints(),
spacing: 6, padding: EdgeInsets.zero,
children: [ visualDensity: VisualDensity.compact,
if (item.metadata['exif'] == null) icon: const Icon(Symbols.hide).padding(all: 6),
Text( onPressed: () {
'#${item.rid}', setState(() => _showOverlay = false);
style: metaTextStyle, }),
), Expanded(
if (item.metadata['exif']?['Model'] != null) child: IgnorePointer(
Text( child: Builder(builder: (context) {
'attachmentShotOn'.tr(args: [ final item = widget.data.elementAt(_page);
item.metadata['exif']?['Model'], final doShowCameraInfo =
]), item.metadata['exif']?['Model'] != null;
style: metaTextStyle, final exif = item.metadata['exif'];
).padding(right: 2), return Column(
if (item.metadata['exif']?['Megapixels'] != children: [
null && if (widget.data.length > 1)
item.metadata['exif']?['Model'] != null) Text(
Text( '${_page + 1}/${widget.data.length}',
'${item.metadata['exif']?['Megapixels']}MP', style:
style: metaTextStyle, GoogleFonts.robotoMono(fontSize: 13),
) ).padding(right: 8),
else if (doShowCameraInfo)
Text( Text(
item.size.formatBytes(), 'attachmentShotOn'
style: metaTextStyle, .tr(args: [exif?['Model']]),
), style: metaTextStyle,
if (item.metadata['width'] != null && textAlign: TextAlign.center,
item.metadata['height'] != null) ),
Text( if (doShowCameraInfo)
'${item.metadata['width']}x${item.metadata['height']}', Row(
style: metaTextStyle, spacing: 4,
), mainAxisSize: MainAxisSize.min,
], children: [
if (exif?['Megapixels'] != null)
Text(
'${exif?['Megapixels']}MP',
style: metaTextStyle,
),
if (exif?['ISO'] != null)
Text(
'ISO${exif['ISO']}',
style: metaTextStyle,
),
if (exif?['FNumber'] != null)
Text(
'f/${exif['FNumber']}',
style: metaTextStyle,
),
],
)
],
);
}),
), ),
), ),
const Gap(4), IconButton(
InkWell( constraints: const BoxConstraints(),
onTap: () { padding: EdgeInsets.zero,
visualDensity: VisualDensity.compact,
icon: Container(
padding: const EdgeInsets.all(6),
child: !_isDownloading
? !_isCompletedDownload
? const Icon(Symbols.save_alt)
: const Icon(Symbols.download_done)
: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
backgroundColor: Theme.of(context)
.colorScheme
.surfaceContainerHighest,
value: _progressOfDownload,
strokeWidth: 3,
),
),
),
onPressed:
_isDownloading ? null : () => _saveToAlbum(_page),
),
IconButton(
iconSize: 18,
constraints: const BoxConstraints(),
icon: const Icon(Icons.info_outline),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Theme.of(context)
.colorScheme
.surface
.withOpacity(0.5),
),
),
onPressed: () {
_showDetail = true; _showDetail = true;
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => _AttachmentZoomDetailPopup( builder: (context) => _AttachmentZoomDetailPopup(
data: widget.data.elementAt( data: widget.data.elementAt(_page),
widget.data.length > 1
? _pageController.page?.round() ?? 0
: 0),
), ),
).then((_) { ).then((_) {
_showDetail = false; _showDetail = false;
}); });
}, },
child: Text(
'viewDetailedAttachment'.tr(),
style: metaTextStyle.copyWith(
decoration: TextDecoration.underline),
),
), ),
], ],
); );
@ -427,18 +386,20 @@ class _AttachmentZoomViewState extends State<AttachmentZoomView> {
), ),
), ),
onTap: () { onTap: () {
if (_showOverlay) {
Navigator.pop(context);
return;
}
setState(() => _showOverlay = !_showOverlay); setState(() => _showOverlay = !_showOverlay);
}, },
onVerticalDragUpdate: (details) { onVerticalDragUpdate: (details) {
if (_showDetail) return; if (_showDetail || !_dismissable) return;
if (details.delta.dy <= -20) { if (details.delta.dy <= -20) {
_showDetail = true; _showDetail = true;
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
builder: (context) => _AttachmentZoomDetailPopup( builder: (context) => _AttachmentZoomDetailPopup(
data: widget.data.elementAt(widget.data.length > 1 data: widget.data.elementAt(_page),
? _pageController.page?.round() ?? 0
: 0),
), ),
).then((_) { ).then((_) {
_showDetail = false; _showDetail = false;