From 2fd93246c7a1dd997779ad4e182128a2e208c694 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 10 Jan 2026 23:02:31 +0800 Subject: [PATCH] :lipstick: Improve the image don't animated the opacity if cached --- .../chat/widgets/message_item_wrapper.dart | 7 +- lib/widgets/content/image.dart | 112 ++++++++++++------ 2 files changed, 76 insertions(+), 43 deletions(-) diff --git a/lib/screens/chat/widgets/message_item_wrapper.dart b/lib/screens/chat/widgets/message_item_wrapper.dart index 2977d263..30e279c9 100644 --- a/lib/screens/chat/widgets/message_item_wrapper.dart +++ b/lib/screens/chat/widgets/message_item_wrapper.dart @@ -7,10 +7,9 @@ import 'package:island/models/chat.dart'; import 'package:island/widgets/chat/message_item.dart'; // Provider to track animated messages to prevent replay -final animatedMessagesProvider = - NotifierProvider>( - AnimatedMessagesNotifier.new, - ); +final animatedMessagesProvider = NotifierProvider.autoDispose( + AnimatedMessagesNotifier.new, +); class AnimatedMessagesNotifier extends Notifier> { @override diff --git a/lib/widgets/content/image.dart b/lib/widgets/content/image.dart index d911b2a1..954b63f9 100644 --- a/lib/widgets/content/image.dart +++ b/lib/widgets/content/image.dart @@ -1,6 +1,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_blurhash/flutter_blurhash.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -29,8 +30,16 @@ class UniversalImage extends HookWidget { @override Widget build(BuildContext context) { final loaded = useState(false); + final isCached = useState(null); final isSvgImage = isSvg || uri.toLowerCase().endsWith('.svg'); + useEffect(() { + DefaultCacheManager().getFileFromCache(uri).then((fileInfo) { + isCached.value = fileInfo != null; + }); + return null; + }, [uri]); + if (isSvgImage) { return SvgPicture.network( uri, @@ -59,44 +68,69 @@ class UniversalImage extends HookWidget { fit: StackFit.expand, children: [ if (blurHash != null) BlurHash(hash: blurHash!), - CachedNetworkImage( - imageUrl: uri, - fit: fit, - width: width, - height: height, - memCacheHeight: cacheHeight, - memCacheWidth: cacheWidth, - progressIndicatorBuilder: (context, url, progress) { - return Center( - child: AnimatedCircularProgressIndicator( - value: progress.progress, - color: Colors.white.withOpacity(0.5), - ), - ); - }, - imageBuilder: (context, imageProvider) { - Future(() { - if (context.mounted) return loaded.value = true; - }); - return AnimatedOpacity( - opacity: loaded.value ? 1.0 : 0.0, - duration: const Duration(milliseconds: 300), - child: Image( - image: imageProvider, - fit: fit, - width: width, - height: height, - ), - ); - }, - errorWidget: (context, url, error) => useFallbackImage - ? Image.asset( - 'assets/images/media-offline.jpg', - fit: BoxFit.cover, - key: Key('image-broke-$uri'), - ) - : SizedBox.shrink(), - ), + if (isCached.value == null) + Center(child: CircularProgressIndicator()) + else if (isCached.value!) + CachedNetworkImage( + imageUrl: uri, + fit: fit, + width: width, + height: height, + memCacheHeight: cacheHeight, + memCacheWidth: cacheWidth, + imageBuilder: (context, imageProvider) => Image( + image: imageProvider, + fit: fit, + width: width, + height: height, + ), + errorWidget: (context, url, error) => useFallbackImage + ? Image.asset( + 'assets/images/media-offline.jpg', + fit: BoxFit.cover, + key: Key('image-broke-$uri'), + ) + : SizedBox.shrink(), + ) + else + CachedNetworkImage( + imageUrl: uri, + fit: fit, + width: width, + height: height, + memCacheHeight: cacheHeight, + memCacheWidth: cacheWidth, + progressIndicatorBuilder: (context, url, progress) { + return Center( + child: AnimatedCircularProgressIndicator( + value: progress.progress, + color: Colors.white.withOpacity(0.5), + ), + ); + }, + imageBuilder: (context, imageProvider) { + Future(() { + if (context.mounted) loaded.value = true; + }); + return AnimatedOpacity( + opacity: loaded.value ? 1.0 : 0.0, + duration: const Duration(milliseconds: 300), + child: Image( + image: imageProvider, + fit: fit, + width: width, + height: height, + ), + ); + }, + errorWidget: (context, url, error) => useFallbackImage + ? Image.asset( + 'assets/images/media-offline.jpg', + fit: BoxFit.cover, + key: Key('image-broke-$uri'), + ) + : SizedBox.shrink(), + ), ], ), ); @@ -138,4 +172,4 @@ class AnimatedCircularProgressIndicator extends HookWidget { backgroundColor: Colors.transparent, ); } -} +} \ No newline at end of file