| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:flutter_svg/flutter_svg.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<CountryIcon> createState() => _CountryIconState();
- }
- class _CountryIconState extends State<CountryIcon> {
- /// 图片路径
- String? _imagePath;
- /// 图片类型:svg 或 png
- String? _imageType;
- /// 是否正在加载
- bool _isLoading = true;
- @override
- void initState() {
- super.initState();
- _findImage();
- }
- @override
- void didUpdateWidget(CountryIcon oldWidget) {
- super.didUpdateWidget(oldWidget);
- if (oldWidget.countryCode != widget.countryCode) {
- _findImage();
- }
- }
- /// 智能查找图片
- Future<void> _findImage() async {
- if (!mounted) return;
- setState(() {
- _isLoading = true;
- });
- final code = widget.countryCode.toLowerCase().trim();
- // 如果为空,直接使用默认图标
- if (code.isEmpty) {
- setState(() {
- _imagePath = 'assets/flags/xx.svg';
- _imageType = 'svg';
- _isLoading = false;
- });
- return;
- }
- // 1. 先检查 flags 目录的 svg
- final flagPath = 'assets/flags/$code.svg';
- if (await _assetExists(flagPath)) {
- setState(() {
- _imagePath = flagPath;
- _imageType = 'svg';
- _isLoading = false;
- });
- return;
- }
- // 2. 检查 streaming 目录的 png
- final streamingPath = 'assets/images/streaming/$code.png';
- if (await _assetExists(streamingPath)) {
- setState(() {
- _imagePath = streamingPath;
- _imageType = 'png';
- _isLoading = false;
- });
- return;
- }
- // 3. 使用默认图标
- setState(() {
- _imagePath = 'assets/flags/xx.svg';
- _imageType = 'svg';
- _isLoading = false;
- });
- }
- /// 检查资源文件是否存在
- Future<bool> _assetExists(String path) async {
- try {
- await rootBundle.load(path);
- return true;
- } catch (e) {
- return false;
- }
- }
- @override
- Widget build(BuildContext context) {
- // 加载中显示占位符
- if (_isLoading || _imagePath == null || _imageType == null) {
- return _buildPlaceholder();
- }
- // 根据图片类型返回不同的组件
- 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(),
- ),
- );
- } else {
- // png 类型使用 IXImage
- return IXImage(
- source: _imagePath!,
- sourceType: ImageSourceType.asset,
- width: widget.width,
- height: widget.height,
- borderRadius: widget.borderRadius,
- fit: widget.fit,
- );
- }
- }
- /// 构建占位符
- 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({
- Key? key,
- required this.countryCode,
- required this.width,
- required this.height,
- this.borderRadius,
- this.fit,
- this.preferStreaming = false,
- }) : super(key: key);
- @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,
- );
- }
- }
|