QinGang_interested/lib/tools/smart_image.dart

180 lines
4.5 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// lib/widgets/smart_image.dart
import 'package:flutter/material.dart';
/// SmartAssetImage
/// - 自动计算目标像素宽度devicePixelRatio * context.width
/// - 使用 ResizeImage 限制解码尺寸以降低首次解码开销
/// - 预缓存precacheImage并提供占位色/小占位Widget
/// - gaplessPlayback 避免重建时闪烁
class SmartAssetImage extends StatefulWidget {
final String assetPath;
final double? height;
final double? width;
final BoxFit fit;
final Color placeholderColor;
final Duration precacheTimeout;
const SmartAssetImage({
Key? key,
required this.assetPath,
this.height,
this.width,
this.fit = BoxFit.cover,
this.placeholderColor = const Color(0xFFE8F4FD),
this.precacheTimeout = const Duration(milliseconds: 800),
}) : super(key: key);
@override
State<SmartAssetImage> createState() => _SmartAssetImageState();
}
class _SmartAssetImageState extends State<SmartAssetImage> {
ImageProvider? _provider;
bool _done = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_prepareImage();
}
Future<void> _prepareImage() async {
// 避免重复处理
if (_done) return;
try {
final mq = MediaQuery.of(context);
final logicalWidth = widget.width ?? mq.size.width;
final devicePixelRatio = mq.devicePixelRatio;
final int targetWidth = (logicalWidth * devicePixelRatio).toInt();
// 使用 ResizeImage 限制解码到合适的像素宽度
final provider = ResizeImage(
AssetImage(widget.assetPath),
width: targetWidth,
);
// attempt to precache but don't block too long
final precacheFuture = precacheImage(provider, context);
// optional: bound the wait time to prevent waiting forever
await precacheFuture
.timeout(widget.precacheTimeout, onTimeout: () => null);
if (mounted) {
setState(() {
_provider = provider;
_done = true;
});
}
} catch (e) {
// 如果预缓存失败,仍然回退到常规 AssetImage
if (mounted) {
setState(() {
_provider = AssetImage(widget.assetPath);
_done = true;
});
}
}
}
@override
Widget build(BuildContext context) {
final placeholder = Container(
width: widget.width ?? double.infinity,
height: widget.height,
color: widget.placeholderColor,
);
if (_provider == null) {
return placeholder;
}
return Image(
image: _provider!,
width: widget.width ?? double.infinity,
height: widget.height,
fit: widget.fit,
gaplessPlayback: true,
);
}
}
/// SmartNetworkImage (无第三方包版本)
/// - 先显示 placeholder / local placeholder
/// - 再通过 precacheImage 加载 NetworkImage
class SmartNetworkImage extends StatefulWidget {
final String url;
final double? height;
final double? width;
final BoxFit fit;
final Widget? placeholder;
final Duration precacheTimeout;
const SmartNetworkImage({
Key? key,
required this.url,
this.height,
this.width,
this.fit = BoxFit.cover,
this.placeholder,
this.precacheTimeout = const Duration(milliseconds: 800),
}) : super(key: key);
@override
State<SmartNetworkImage> createState() => _SmartNetworkImageState();
}
class _SmartNetworkImageState extends State<SmartNetworkImage> {
ImageProvider? _provider;
bool _done = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_prepare();
}
Future<void> _prepare() async {
if (_done) return;
try {
final provider = NetworkImage(widget.url);
await precacheImage(provider, context).timeout(widget.precacheTimeout, onTimeout: () => null);
if (mounted) {
setState(() {
_provider = provider;
_done = true;
});
}
} catch (e) {
if (mounted) {
setState(() {
_provider = NetworkImage(widget.url); // fallback
_done = true;
});
}
}
}
@override
Widget build(BuildContext context) {
final placeholder = widget.placeholder ??
Container(
width: widget.width ?? double.infinity,
height: widget.height,
color: const Color(0xFFE8F4FD),
);
if (_provider == null) {
return placeholder;
}
return Image(
image: _provider!,
width: widget.width ?? double.infinity,
height: widget.height,
fit: widget.fit,
gaplessPlayback: true,
);
}
}