From 1c510d63feb34a09ce46eabe1c18ff950934ca52 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 6 Mar 2025 22:46:02 +0800 Subject: [PATCH] :bug: Fix share via image errored --- lib/widgets/attachment/attachment_item.dart | 70 ++++++--- lib/widgets/attachment/attachment_list.dart | 150 ++++++++++++-------- lib/widgets/post/post_item.dart | 7 +- lib/widgets/universal_image.dart | 22 ++- 4 files changed, 165 insertions(+), 84 deletions(-) diff --git a/lib/widgets/attachment/attachment_item.dart b/lib/widgets/attachment/attachment_item.dart index a9740c5..2548788 100644 --- a/lib/widgets/attachment/attachment_item.dart +++ b/lib/widgets/attachment/attachment_item.dart @@ -22,12 +22,14 @@ class AttachmentItem extends StatelessWidget { final SnAttachment? data; final String? heroTag; final BoxFit fit; + final FilterQuality? filterQuality; const AttachmentItem({ super.key, this.fit = BoxFit.cover, required this.data, required this.heroTag, + this.filterQuality, }); Widget _buildContent(BuildContext context) { @@ -47,6 +49,7 @@ class AttachmentItem extends StatelessWidget { sn.getAttachmentUrl(data!.rid), key: Key('attachment-${data!.rid}-$tag'), fit: fit, + filterQuality: filterQuality, ), ); case 'video': @@ -83,13 +86,16 @@ class _AttachmentItemSensitiveBlur extends StatefulWidget { final Widget child; final bool isCompact; - const _AttachmentItemSensitiveBlur({required this.child, this.isCompact = false}); + const _AttachmentItemSensitiveBlur( + {required this.child, this.isCompact = false}); @override - State<_AttachmentItemSensitiveBlur> createState() => _AttachmentItemSensitiveBlurState(); + State<_AttachmentItemSensitiveBlur> createState() => + _AttachmentItemSensitiveBlurState(); } -class _AttachmentItemSensitiveBlurState extends State<_AttachmentItemSensitiveBlur> { +class _AttachmentItemSensitiveBlurState + extends State<_AttachmentItemSensitiveBlur> { bool _doesShow = false; @override @@ -124,10 +130,15 @@ class _AttachmentItemSensitiveBlurState extends State<_AttachmentItemSensitiveBl Text( 'sensitiveContentDescription', textAlign: TextAlign.center, - ).tr().fontSize(14).textColor(Colors.white.withOpacity(0.8)), + ) + .tr() + .fontSize(14) + .textColor(Colors.white.withOpacity(0.8)), if (!widget.isCompact) const Gap(16), InkWell( - child: Text('sensitiveContentReveal').tr().textColor(Colors.white), + child: Text('sensitiveContentReveal') + .tr() + .textColor(Colors.white), onTap: () { setState(() => _doesShow = !_doesShow); }, @@ -137,7 +148,9 @@ class _AttachmentItemSensitiveBlurState extends State<_AttachmentItemSensitiveBl ).center(), ), ), - ).opacity(_doesShow ? 0 : 1, animate: true).animate(const Duration(milliseconds: 300), Curves.easeInOut), + ) + .opacity(_doesShow ? 0 : 1, animate: true) + .animate(const Duration(milliseconds: 300), Curves.easeInOut), if (_doesShow) Positioned( top: 0, @@ -174,10 +187,12 @@ class _AttachmentItemContentVideo extends StatefulWidget { }); @override - State<_AttachmentItemContentVideo> createState() => _AttachmentItemContentVideoState(); + State<_AttachmentItemContentVideo> createState() => + _AttachmentItemContentVideoState(); } -class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo> { +class _AttachmentItemContentVideoState + extends State<_AttachmentItemContentVideo> { bool _showContent = false; bool _showOriginal = false; @@ -188,7 +203,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo setState(() => _showContent = true); MediaKit.ensureInitialized(); final sn = context.read(); - final url = _showOriginal ? sn.getAttachmentUrl(widget.data.rid) : sn.getAttachmentUrl(widget.data.compressed!.rid); + final url = _showOriginal + ? sn.getAttachmentUrl(widget.data.rid) + : sn.getAttachmentUrl(widget.data.compressed!.rid); _videoPlayer = Player(); _videoController = VideoController(_videoPlayer!); _videoPlayer!.open(Media(url), play: !widget.isAutoload); @@ -201,7 +218,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo final sn = context.read(); _videoPlayer?.open( Media( - _showOriginal ? sn.getAttachmentUrl(widget.data.rid) : sn.getAttachmentUrl(widget.data.compressed!.rid), + _showOriginal + ? sn.getAttachmentUrl(widget.data.rid) + : sn.getAttachmentUrl(widget.data.compressed!.rid), ), play: true, ); @@ -283,7 +302,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo ), Text( Duration( - milliseconds: (widget.data.data['duration'] ?? 0).toInt() * 1000, + milliseconds: + (widget.data.data['duration'] ?? 0).toInt() * + 1000, ).toString(), style: GoogleFonts.robotoMono( fontSize: 12, @@ -346,7 +367,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo MaterialDesktopCustomButton( iconSize: 24, onPressed: _toggleOriginal, - icon: _showOriginal ? const Icon(Symbols.high_quality, size: 24) : const Icon(Symbols.sd, size: 24), + icon: _showOriginal + ? const Icon(Symbols.high_quality, size: 24) + : const Icon(Symbols.sd, size: 24), ), ], ), @@ -354,8 +377,9 @@ class _AttachmentItemContentVideoState extends State<_AttachmentItemContentVideo child: Video( controller: _videoController!, aspectRatio: ratio, - controls: - !kIsWeb && (Platform.isAndroid || Platform.isIOS) ? MaterialVideoControls : MaterialDesktopVideoControls, + controls: !kIsWeb && (Platform.isAndroid || Platform.isIOS) + ? MaterialVideoControls + : MaterialDesktopVideoControls, ), ), ); @@ -378,10 +402,12 @@ class _AttachmentItemContentAudio extends StatefulWidget { }); @override - State<_AttachmentItemContentAudio> createState() => _AttachmentItemContentAudioState(); + State<_AttachmentItemContentAudio> createState() => + _AttachmentItemContentAudioState(); } -class _AttachmentItemContentAudioState extends State<_AttachmentItemContentAudio> { +class _AttachmentItemContentAudioState + extends State<_AttachmentItemContentAudio> { bool _showContent = false; double? _draggingValue; @@ -552,8 +578,12 @@ class _AttachmentItemContentAudioState extends State<_AttachmentItemContentAudio overlayShape: SliderComponentShape.noOverlay, ), child: Slider( - secondaryTrackValue: _bufferedPosition.inMilliseconds.abs().toDouble(), - value: _draggingValue?.abs() ?? _position.inMilliseconds.toDouble().abs(), + secondaryTrackValue: _bufferedPosition + .inMilliseconds + .abs() + .toDouble(), + value: _draggingValue?.abs() ?? + _position.inMilliseconds.toDouble().abs(), min: 0, max: math .max( @@ -593,7 +623,9 @@ class _AttachmentItemContentAudioState extends State<_AttachmentItemContentAudio ), const Gap(16), IconButton.filled( - icon: _isPlaying ? const Icon(Symbols.pause) : const Icon(Symbols.play_arrow), + icon: _isPlaying + ? const Icon(Symbols.pause) + : const Icon(Symbols.play_arrow), onPressed: () { _audioPlayer!.playOrPause(); }, diff --git a/lib/widgets/attachment/attachment_list.dart b/lib/widgets/attachment/attachment_list.dart index fcb7fd2..1baa49f 100644 --- a/lib/widgets/attachment/attachment_list.dart +++ b/lib/widgets/attachment/attachment_list.dart @@ -21,6 +21,7 @@ class AttachmentList extends StatefulWidget { final double? minWidth; final double? maxWidth; final EdgeInsets? padding; + final FilterQuality? filterQuality; const AttachmentList({ super.key, @@ -33,23 +34,27 @@ class AttachmentList extends StatefulWidget { this.minWidth, this.maxWidth, this.padding, + this.filterQuality, }); - static const BorderRadius kDefaultRadius = BorderRadius.all(Radius.circular(8)); + static const BorderRadius kDefaultRadius = + BorderRadius.all(Radius.circular(8)); @override State createState() => _AttachmentListState(); } class _AttachmentListState extends State { - late final List heroTags = List.generate(widget.data.length, (_) => const Uuid().v4()); + late final List heroTags = + List.generate(widget.data.length, (_) => const Uuid().v4()); @override Widget build(BuildContext context) { return LayoutBuilder( builder: (context, layoutConstraints) { - final borderSide = - widget.bordered ? BorderSide(width: 1, color: Theme.of(context).dividerColor) : BorderSide.none; + final borderSide = widget.bordered + ? BorderSide(width: 1, color: Theme.of(context).dividerColor) + : BorderSide.none; final backgroundColor = Theme.of(context).colorScheme.surfaceContainer; final constraints = BoxConstraints( minWidth: widget.minWidth ?? 80, @@ -58,13 +63,13 @@ class _AttachmentListState extends State { if (widget.data.isEmpty) return const SizedBox.shrink(); if (widget.data.length == 1) { - final singleAspectRatio = - widget.data[0]?.data['ratio']?.toDouble() ?? + final singleAspectRatio = widget.data[0]?.data['ratio']?.toDouble() ?? switch (widget.data[0]?.mimetype.split('/').firstOrNull) { 'audio' => 16 / 9, 'video' => 16 / 9, _ => 1, - }.toDouble(); + } + .toDouble(); return Container( padding: widget.padding ?? EdgeInsets.zero, @@ -80,12 +85,18 @@ class _AttachmentListState extends State { ), child: ClipRRect( borderRadius: AttachmentList.kDefaultRadius, - child: AttachmentItem(data: widget.data[0], heroTag: heroTags[0], fit: widget.fit), + child: AttachmentItem( + data: widget.data[0], + heroTag: heroTags[0], + fit: widget.fit, + filterQuality: widget.filterQuality, + ), ), ), ), onTap: () { - if (widget.data.firstOrNull?.mediaType != SnMediaType.image) return; + if (widget.data.firstOrNull?.mediaType != SnMediaType.image) + return; context.pushTransparentRoute( AttachmentZoomView( data: widget.data.where((ele) => ele != null).cast(), @@ -100,8 +111,10 @@ class _AttachmentListState extends State { ); } - final fullOfImage = - widget.data.where((ele) => ele?.mediaType == SnMediaType.image).length == widget.data.length; + final fullOfImage = widget.data + .where((ele) => ele?.mediaType == SnMediaType.image) + .length == + widget.data.length; if (widget.gridded && fullOfImage) { return Container( @@ -117,29 +130,36 @@ class _AttachmentListState extends State { crossAxisCount: math.min(widget.data.length, 2), crossAxisSpacing: 4, mainAxisSpacing: 4, - children: - widget.data - .mapIndexed( - (idx, ele) => GestureDetector( - child: Container( - constraints: constraints, - child: AttachmentItem(data: ele, heroTag: heroTags[idx], fit: BoxFit.cover), - ), - onTap: () { - if (widget.data[idx]!.mediaType != SnMediaType.image) return; - context.pushTransparentRoute( - AttachmentZoomView( - data: widget.data.where((ele) => ele != null).cast(), - initialIndex: idx, - heroTags: heroTags, - ), - backgroundColor: Colors.black.withOpacity(0.7), - rootNavigator: true, - ); - }, + children: widget.data + .mapIndexed( + (idx, ele) => GestureDetector( + child: Container( + constraints: constraints, + child: AttachmentItem( + data: ele, + heroTag: heroTags[idx], + fit: BoxFit.cover, + filterQuality: widget.filterQuality, ), - ) - .toList(), + ), + onTap: () { + if (widget.data[idx]!.mediaType != SnMediaType.image) + return; + context.pushTransparentRoute( + AttachmentZoomView( + data: widget.data + .where((ele) => ele != null) + .cast(), + initialIndex: idx, + heroTags: heroTags, + ), + backgroundColor: Colors.black.withOpacity(0.7), + rootNavigator: true, + ); + }, + ), + ) + .toList(), ), ), ); @@ -156,22 +176,26 @@ class _AttachmentListState extends State { child: ClipRRect( borderRadius: AttachmentList.kDefaultRadius, child: Column( - children: - widget.data - .mapIndexed( - (idx, ele) => GestureDetector( - child: AspectRatio( - aspectRatio: ele?.data['ratio']?.toDouble() ?? 1, - child: Container( - constraints: constraints, - child: AttachmentItem(data: ele, heroTag: heroTags[idx], fit: BoxFit.cover), - ), + children: widget.data + .mapIndexed( + (idx, ele) => GestureDetector( + child: AspectRatio( + aspectRatio: ele?.data['ratio']?.toDouble() ?? 1, + child: Container( + constraints: constraints, + child: AttachmentItem( + data: ele, + heroTag: heroTags[idx], + fit: BoxFit.cover, + filterQuality: widget.filterQuality, ), ), - ) - .expand((ele) => [ele, const Divider(height: 1)]) - .toList() - ..removeLast(), + ), + ), + ) + .expand((ele) => [ele, const Divider(height: 1)]) + .toList() + ..removeLast(), ), ), ); @@ -189,16 +213,22 @@ class _AttachmentListState extends State { itemCount: widget.data.length, itemBuilder: (context, idx) { return Container( - constraints: constraints.copyWith(maxWidth: widget.maxWidth), + constraints: + constraints.copyWith(maxWidth: widget.maxWidth), child: AspectRatio( - aspectRatio: (widget.data[idx]?.data['ratio'] ?? 1).toDouble(), + aspectRatio: + (widget.data[idx]?.data['ratio'] ?? 1).toDouble(), child: GestureDetector( onTap: () { - if (widget.data[idx]?.mediaType != SnMediaType.image) return; + if (widget.data[idx]?.mediaType != SnMediaType.image) + return; context.pushTransparentRoute( AttachmentZoomView( - data: - widget.data.where((ele) => ele != null && ele.mediaType == SnMediaType.image).cast(), + data: widget.data + .where((ele) => + ele != null && + ele.mediaType == SnMediaType.image) + .cast(), initialIndex: idx, heroTags: heroTags, ), @@ -212,18 +242,25 @@ class _AttachmentListState extends State { Container( decoration: BoxDecoration( color: backgroundColor, - border: Border(top: borderSide, bottom: borderSide), + border: + Border(top: borderSide, bottom: borderSide), borderRadius: AttachmentList.kDefaultRadius, ), child: ClipRRect( borderRadius: AttachmentList.kDefaultRadius, - child: AttachmentItem(data: widget.data[idx], heroTag: heroTags[idx]), + child: AttachmentItem( + data: widget.data[idx], + heroTag: heroTags[idx], + filterQuality: widget.filterQuality, + ), ), ), Positioned( right: 8, bottom: 8, - child: Chip(label: Text('${idx + 1}/${widget.data.length}')), + child: Chip( + label: + Text('${idx + 1}/${widget.data.length}')), ), ], ), @@ -245,5 +282,6 @@ class _AttachmentListState extends State { class _AttachmentListScrollBehavior extends MaterialScrollBehavior { @override - Set get dragDevices => {PointerDeviceKind.touch, PointerDeviceKind.mouse}; + Set get dragDevices => + {PointerDeviceKind.touch, PointerDeviceKind.mouse}; } diff --git a/lib/widgets/post/post_item.dart b/lib/widgets/post/post_item.dart index d912ba9..a23ad1e 100644 --- a/lib/widgets/post/post_item.dart +++ b/lib/widgets/post/post_item.dart @@ -149,7 +149,6 @@ class PostItem extends StatelessWidget { void _doShareViaPicture(BuildContext context) async { final box = context.findRenderObject() as RenderBox?; - context.showSnackbar('postSharingViaPicture'.tr()); final controller = ScreenshotController(); final capturedImage = await controller.captureFromLongWidget( @@ -160,9 +159,9 @@ class PostItem extends StatelessWidget { child: Material( child: MultiProvider( providers: [ + // Create a copy of environments Provider(create: (_) => context.read()), - ChangeNotifierProvider( - create: (_) => context.read()), + Provider(create: (_) => context.read()), ], child: ResponsiveBreakpoints.builder( breakpoints: ResponsiveBreakpoints.of(context).breakpoints, @@ -507,6 +506,8 @@ class PostShareImageWidget extends StatelessWidget { StyledWidget(AttachmentList( data: data.preload!.attachments!, columned: true, + fit: BoxFit.contain, + filterQuality: FilterQuality.high, )).padding(horizontal: 16, bottom: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/widgets/universal_image.dart b/lib/widgets/universal_image.dart index 0adab33..5935c10 100644 --- a/lib/widgets/universal_image.dart +++ b/lib/widgets/universal_image.dart @@ -34,11 +34,14 @@ class UniversalImage extends StatelessWidget { @override Widget build(BuildContext context) { final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; - final double? resizeHeight = cacheHeight != null ? (cacheHeight! * devicePixelRatio) : null; - final double? resizeWidth = cacheWidth != null ? (cacheWidth! * devicePixelRatio) : null; + final double? resizeHeight = + cacheHeight != null ? (cacheHeight! * devicePixelRatio) : null; + final double? resizeWidth = + cacheWidth != null ? (cacheWidth! * devicePixelRatio) : null; return Image( - filterQuality: filterQuality ?? context.read().imageQuality, + filterQuality: + filterQuality ?? context.read().imageQuality, image: kIsWeb ? UniversalImage.provider(url) : ResizeImage( @@ -52,7 +55,8 @@ class UniversalImage extends StatelessWidget { fit: fit, loadingBuilder: noProgressIndicator ? null - : (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { + : (BuildContext context, Widget child, + ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child; return Container( constraints: BoxConstraints(maxHeight: 80), @@ -61,12 +65,15 @@ class UniversalImage extends StatelessWidget { tween: Tween( begin: 0, end: loadingProgress.expectedTotalBytes != null - ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! + ? loadingProgress.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! : 0, ), duration: const Duration(milliseconds: 300), builder: (context, value, _) => CircularProgressIndicator( - value: loadingProgress.expectedTotalBytes != null ? value.toDouble() : null, + value: loadingProgress.expectedTotalBytes != null + ? value.toDouble() + : null, ), ), ), @@ -114,6 +121,7 @@ class AutoResizeUniversalImage extends StatelessWidget { final BoxFit? fit; final bool noProgressIndicator; final bool noErrorWidget; + final FilterQuality? filterQuality; const AutoResizeUniversalImage( this.url, { @@ -123,6 +131,7 @@ class AutoResizeUniversalImage extends StatelessWidget { this.fit, this.noProgressIndicator = false, this.noErrorWidget = false, + this.filterQuality, }); @override @@ -137,6 +146,7 @@ class AutoResizeUniversalImage extends StatelessWidget { noErrorWidget: noErrorWidget, cacheHeight: constraints.maxHeight, cacheWidth: constraints.maxWidth, + filterQuality: filterQuality, ); }); }