import 'package:flutter/widgets.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../../core/theme/app_colors.dart'; /// 图标组件,对应 uni-app 版页面里的 ``。 /// /// **为什么是混合方案**:源工程只提供了 8 个真实字形(`iconfont.ttf`),而页面里 /// 引用的 `icon-lightning` / `icon-location` / `icon-star` 等**在字体里并不存在**, /// 源工程实际渲染为空白。为保证复刻后的界面可读可用,这里: /// - 8 个真实字形继续用搬运过来的 `iconfont.ttf` 渲染,保持逐像素一致; /// - 其余语义图标改用 SVG 路径绘制,线条风格与源设计语言保持一致。 class XIcon extends StatelessWidget { const XIcon( this.name, { super.key, this.size = 20, this.color = AppColors.textMuted, }); final String name; final double size; final Color color; /// `iconfont.ttf` 中真实存在的字形 -> Unicode 码点(取自源 `iconfont.css`)。 static const Map glyphs = { 'shandian': 0xe6a4, 'tongzhi': 0xe60a, 'qianbao': 0xe60b, 'yaoqing': 0x100a5, 'dingdan': 0xe62a, 'weixin': 0xe665, 'wode': 0xe607, 'shouyi': 0x10186, }; /// 语义图标的 SVG 路径(24×24 视图框,`currentColor` 会被替换成实际色值)。 static const Map paths = { 'bolt': '', 'location': '', 'clock': '', 'filter': '', 'map': '', 'refresh': '', 'check': '', 'forward': '', 'back': '', 'arrow-down': '', 'star': '', 'verified': '', 'notification': '', 'share': '', 'navigation': '', 'call': '', 'add-photo': '', 'info': '', 'warning': '', 'wallet': '', 'list': '', 'settings': '', 'trend-up': '', 'close': '', 'person': '', 'camera': '', }; /// 源里两种命名风格混用(uni-app 的 `icon-xxx` 与 Material Symbols 名), /// 这里统一做别名收敛,调用方两种写法都能渲染。 static const Map _aliases = { 'lightning': 'bolt', 'time': 'clock', 'cert': 'verified', 'notify': 'notification', 'arrow-right': 'forward', 'order': 'list', 'earnings': 'wallet', 'chevron_left': 'back', 'location_on': 'location', 'directions': 'navigation', 'add_a_photo': 'add-photo', }; static final Map _svgCache = {}; static String _hex(Color color) => '#${(color.toARGB32() & 0xFFFFFF).toRadixString(16).padLeft(6, '0')}'; /// 归一化后的语义名:别名 -> 目标名。 static String resolve(String name) => _aliases[name] ?? name; /// 生成 (图标, 颜色) 组合对应的 SVG 字符串,带缓存避免重复拼接。 static String svgFor(String name, Color color) { final key = '$name|${color.toARGB32()}'; return _svgCache.putIfAbsent(key, () { final body = (paths[resolve(name)] ?? paths['close']!).replaceAll( 'currentColor', _hex(color), ); return '$body'; }); } @override Widget build(BuildContext context) { final glyph = glyphs[name]; if (glyph != null) { // 源里的彩色字形:直接交给 iconfont.ttf 渲染,保留原字形配色。 return SizedBox( width: size, height: size, child: Center( child: Text( String.fromCharCode(glyph), style: TextStyle( fontFamily: 'iconfont', fontSize: size, height: 1, color: color, ), ), ), ); } return SizedBox( width: size, height: size, child: SvgPicture.string( svgFor(name, color), width: size, height: size, fit: BoxFit.contain, ), ); } }