import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:nomo/app/extensions/widget_extension.dart'; import 'ix_image.dart'; /// 国旗/图标组件 /// /// 智能查找逻辑: /// 1. 优先从 assets/flags/{countryCode}.svg 查找 /// 2. 找不到则从 assets/images/streaming/{countryCode}.png 查找 /// 3. 都找不到则显示默认图标 assets/flags/xx.svg class CountryIcon extends StatefulWidget { /// 国家/地区代码(如: us, cn, netflix 等) final String countryCode; /// 宽度 final double width; /// 高度 final double height; /// 圆角 final double? borderRadius; /// 适配方式 final BoxFit? fit; const CountryIcon({ super.key, required this.countryCode, required this.width, required this.height, this.borderRadius, this.fit, }); @override State createState() => _CountryIconState(); } class _CountryIconState extends State { /// 静态缓存:存储已查找过的图片路径,避免重复异步检查 static final Map _imageCache = {}; /// 图片路径 String? _imagePath; /// 图片类型:svg 或 png String? _imageType; /// 是否首次加载(仅首次加载时显示占位符) bool _isFirstLoad = true; @override void initState() { super.initState(); _findImage(); } @override void didUpdateWidget(CountryIcon oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.countryCode != widget.countryCode) { _findImage(); } } /// 智能查找图片 Future _findImage() async { if (!mounted) return; final code = widget.countryCode.toLowerCase().trim(); // 如果为空,直接使用默认图标 if (code.isEmpty) { _updateImage('assets/flags/xx.svg', 'svg'); return; } // 检查缓存,如果有缓存则直接使用,避免异步等待 if (_imageCache.containsKey(code)) { final cached = _imageCache[code]!; _updateImage(cached.path, cached.type); return; } // 异步查找图片(不设置 loading 状态,保持旧图片显示) String? foundPath; String? foundType; // 1. 先检查 flags 目录的 svg final flagPath = 'assets/flags/$code.svg'; if (await _assetExists(flagPath)) { foundPath = flagPath; foundType = 'svg'; } else { // 2. 检查 streaming 目录的 png final streamingPath = 'assets/images/streaming/$code.png'; if (await _assetExists(streamingPath)) { foundPath = streamingPath; foundType = 'png'; } else { // 3. 使用默认图标 foundPath = 'assets/flags/xx.svg'; foundType = 'svg'; } } // 缓存结果 _imageCache[code] = _ImageInfo(foundPath, foundType); // 检查 widget 是否仍然需要这个 code 的图片(防止快速切换时的竞态) if (!mounted || widget.countryCode.toLowerCase().trim() != code) return; _updateImage(foundPath, foundType); } /// 更新图片状态 void _updateImage(String path, String type) { if (!mounted) return; setState(() { _imagePath = path; _imageType = type; _isFirstLoad = false; }); } /// 检查资源文件是否存在 Future _assetExists(String path) async { try { await rootBundle.load(path); return true; } catch (e) { return false; } } @override Widget build(BuildContext context) { // 仅首次加载时显示占位符,切换时保持旧图片 if (_isFirstLoad || _imagePath == null || _imageType == null) { return _buildPlaceholder().withShadow( borderRadius: widget.borderRadius ?? 0, ); } // 根据图片类型返回不同的组件 if (_imageType == 'svg') { return ClipRRect( borderRadius: BorderRadius.circular(widget.borderRadius ?? 0), child: SvgPicture.asset( _imagePath!, width: widget.width, height: widget.height, fit: widget.fit ?? BoxFit.cover, placeholderBuilder: (context) => _buildPlaceholder(), ), ).withShadow(borderRadius: widget.borderRadius ?? 0); } else { // png 类型使用 IXImage return IXImage( source: _imagePath!, sourceType: ImageSourceType.asset, width: widget.width, height: widget.height, borderRadius: widget.borderRadius, fit: widget.fit, ).withShadow(borderRadius: widget.borderRadius ?? 0); } } /// 构建占位符 Widget _buildPlaceholder() { return Container( width: widget.width, height: widget.height, decoration: BoxDecoration( color: Colors.grey.withValues(alpha: 0.2), borderRadius: BorderRadius.circular(widget.borderRadius ?? 0), ), ); } } /// 国旗/图标组件的简化版本(同步加载,不检查文件是否存在) /// /// 性能更好,但可能在资源不存在时显示错误 class CountryIconFast extends StatelessWidget { /// 国家/地区代码 final String countryCode; /// 宽度 final double width; /// 高度 final double height; /// 圆角 final double? borderRadius; /// 适配方式 final BoxFit? fit; /// 是否优先使用 streaming 目录 final bool preferStreaming; const CountryIconFast({ super.key, required this.countryCode, required this.width, required this.height, this.borderRadius, this.fit, this.preferStreaming = false, }); @override Widget build(BuildContext context) { final code = countryCode.toLowerCase().trim(); if (code.isEmpty) { return _buildSvg('assets/flags/xx.svg'); } // 如果优先使用 streaming if (preferStreaming) { return _buildPng('assets/images/streaming/$code.png'); } // 默认优先使用 flags return _buildSvg('assets/flags/$code.svg'); } Widget _buildSvg(String path) { return ClipRRect( borderRadius: BorderRadius.circular(borderRadius ?? 0), child: SvgPicture.asset( path, width: width, height: height, fit: fit ?? BoxFit.cover, placeholderBuilder: (context) => _buildFallback(), ), ); } Widget _buildPng(String path) { return IXImage( source: path, sourceType: ImageSourceType.asset, width: width, height: height, borderRadius: borderRadius, fit: fit, ); } Widget _buildFallback() { // 降级到默认图标 return SvgPicture.asset( 'assets/flags/xx.svg', width: width, height: height, fit: fit ?? BoxFit.cover, ); } } /// 图片信息缓存类 class _ImageInfo { final String path; final String type; _ImageInfo(this.path, this.type); }