// 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 createState() => _SmartAssetImageState(); } class _SmartAssetImageState extends State { ImageProvider? _provider; bool _done = false; @override void didChangeDependencies() { super.didChangeDependencies(); _prepareImage(); } Future _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 createState() => _SmartNetworkImageState(); } class _SmartNetworkImageState extends State { ImageProvider? _provider; bool _done = false; @override void didChangeDependencies() { super.didChangeDependencies(); _prepare(); } Future _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, ); } }