Files
jinzhi/lib/common/widgets/app_network_image.dart

102 lines
2.8 KiB
Dart

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
/// 金值统一的网络图片入口。
///
/// 图片会自动使用 [CachedNetworkImage] 的磁盘缓存;业务页面不应直接创建
/// `Image.network`,以保证加载和失败状态的视觉及缓存策略一致。
class AppNetworkImage extends StatelessWidget {
const AppNetworkImage({
super.key,
required this.imageUrl,
this.width,
this.height,
this.fit = BoxFit.cover,
this.alignment = Alignment.center,
this.borderRadius,
this.cacheKey,
this.placeholder,
this.errorWidget,
this.semanticLabel,
});
final String imageUrl;
final double? width;
final double? height;
final BoxFit fit;
final Alignment alignment;
final BorderRadius? borderRadius;
final String? cacheKey;
final Widget? placeholder;
final Widget? errorWidget;
final String? semanticLabel;
@override
Widget build(BuildContext context) {
final child = imageUrl.trim().isEmpty
? _ImageFallback(errorWidget: errorWidget)
: CachedNetworkImage(
imageUrl: imageUrl,
cacheKey: cacheKey,
width: width,
height: height,
fit: fit,
alignment: alignment,
fadeInDuration: const Duration(milliseconds: 150),
placeholder: (_, _) =>
placeholder ?? const _ImageLoadingPlaceholder(),
errorWidget: (_, _, _) => _ImageFallback(errorWidget: errorWidget),
);
final clipped = borderRadius == null
? child
: ClipRRect(borderRadius: borderRadius!, child: child);
if (semanticLabel == null) return clipped;
return Semantics(image: true, label: semanticLabel, child: clipped);
}
}
class _ImageLoadingPlaceholder extends StatelessWidget {
const _ImageLoadingPlaceholder();
@override
Widget build(BuildContext context) {
final colors = ShadTheme.of(context).colorScheme;
return ColoredBox(
color: colors.muted,
child: Center(
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
color: colors.mutedForeground,
),
),
),
);
}
}
class _ImageFallback extends StatelessWidget {
const _ImageFallback({required this.errorWidget});
final Widget? errorWidget;
@override
Widget build(BuildContext context) {
if (errorWidget != null) return errorWidget!;
final colors = ShadTheme.of(context).colorScheme;
return ColoredBox(
color: colors.muted,
child: Center(
child: Icon(
Icons.image_not_supported_outlined,
color: colors.mutedForeground,
),
),
);
}
}