💄 Improve the image don't animated the opacity if cached

This commit is contained in:
2026-01-10 23:02:31 +08:00
parent 1b2620e957
commit 2fd93246c7
2 changed files with 76 additions and 43 deletions

View File

@@ -7,10 +7,9 @@ import 'package:island/models/chat.dart';
import 'package:island/widgets/chat/message_item.dart'; import 'package:island/widgets/chat/message_item.dart';
// Provider to track animated messages to prevent replay // Provider to track animated messages to prevent replay
final animatedMessagesProvider = final animatedMessagesProvider = NotifierProvider.autoDispose(
NotifierProvider<AnimatedMessagesNotifier, Set<String>>( AnimatedMessagesNotifier.new,
AnimatedMessagesNotifier.new, );
);
class AnimatedMessagesNotifier extends Notifier<Set<String>> { class AnimatedMessagesNotifier extends Notifier<Set<String>> {
@override @override

View File

@@ -1,6 +1,7 @@
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_blurhash/flutter_blurhash.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_hooks/flutter_hooks.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
@@ -29,8 +30,16 @@ class UniversalImage extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loaded = useState(false); final loaded = useState(false);
final isCached = useState<bool?>(null);
final isSvgImage = isSvg || uri.toLowerCase().endsWith('.svg'); final isSvgImage = isSvg || uri.toLowerCase().endsWith('.svg');
useEffect(() {
DefaultCacheManager().getFileFromCache(uri).then((fileInfo) {
isCached.value = fileInfo != null;
});
return null;
}, [uri]);
if (isSvgImage) { if (isSvgImage) {
return SvgPicture.network( return SvgPicture.network(
uri, uri,
@@ -59,44 +68,69 @@ class UniversalImage extends HookWidget {
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
if (blurHash != null) BlurHash(hash: blurHash!), if (blurHash != null) BlurHash(hash: blurHash!),
CachedNetworkImage( if (isCached.value == null)
imageUrl: uri, Center(child: CircularProgressIndicator())
fit: fit, else if (isCached.value!)
width: width, CachedNetworkImage(
height: height, imageUrl: uri,
memCacheHeight: cacheHeight, fit: fit,
memCacheWidth: cacheWidth, width: width,
progressIndicatorBuilder: (context, url, progress) { height: height,
return Center( memCacheHeight: cacheHeight,
child: AnimatedCircularProgressIndicator( memCacheWidth: cacheWidth,
value: progress.progress, imageBuilder: (context, imageProvider) => Image(
color: Colors.white.withOpacity(0.5), image: imageProvider,
), fit: fit,
); width: width,
}, height: height,
imageBuilder: (context, imageProvider) { ),
Future(() { errorWidget: (context, url, error) => useFallbackImage
if (context.mounted) return loaded.value = true; ? Image.asset(
}); 'assets/images/media-offline.jpg',
return AnimatedOpacity( fit: BoxFit.cover,
opacity: loaded.value ? 1.0 : 0.0, key: Key('image-broke-$uri'),
duration: const Duration(milliseconds: 300), )
child: Image( : SizedBox.shrink(),
image: imageProvider, )
fit: fit, else
width: width, CachedNetworkImage(
height: height, imageUrl: uri,
), fit: fit,
); width: width,
}, height: height,
errorWidget: (context, url, error) => useFallbackImage memCacheHeight: cacheHeight,
? Image.asset( memCacheWidth: cacheWidth,
'assets/images/media-offline.jpg', progressIndicatorBuilder: (context, url, progress) {
fit: BoxFit.cover, return Center(
key: Key('image-broke-$uri'), child: AnimatedCircularProgressIndicator(
) value: progress.progress,
: SizedBox.shrink(), 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, backgroundColor: Colors.transparent,
); );
} }
} }