From a2db625ec7f73dfa92feebe03268a5c2e7bcf2b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E6=98=B1=E9=9C=96?= Date: Wed, 1 Dec 2021 15:39:56 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8C=9F:=20=E5=A2=9E=E5=8A=A0=E9=80=8F?= =?UTF-8?q?=E6=98=8E=E5=BA=A6=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/generateComponent.ts | 3 +++ src/templates/Icon.null.safety.dart.template | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libs/generateComponent.ts b/src/libs/generateComponent.ts index a690e82..3e2b445 100644 --- a/src/libs/generateComponent.ts +++ b/src/libs/generateComponent.ts @@ -100,12 +100,15 @@ const addAttribute = (domName: string, sub: XmlData['svg']['symbol'][number]['pa // Set default color same as in iconfont.cn // And create placeholder to inject color by user's behavior sub.$.fill = sub.$.fill || '#333333'; + sub.$['fill-opacity'] = sub.$['fill-opacity'] || 1; } for (const attributeName of Object.keys(sub.$)) { if (attributeName === 'fill') { template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}="''' + getColor(${counter.colorIndex}, color, colors, '${sub.$[attributeName]}') + '''"`; counter.colorIndex += 1; + } else if (attributeName === 'fill-opacity') { + template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}=$opacity`; } else { template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}="${sub.$[attributeName]}"`; } diff --git a/src/templates/Icon.null.safety.dart.template b/src/templates/Icon.null.safety.dart.template index 550d273..d869f04 100644 --- a/src/templates/Icon.null.safety.dart.template +++ b/src/templates/Icon.null.safety.dart.template @@ -29,8 +29,8 @@ class IconFont extends StatelessWidget { final String? color; final List? colors; final double size; - - IconFont(dynamic iconName, { this.size = #size#, this.color, this.colors }) { + final double? opacity; + IconFont(dynamic iconName, { this.size = #size#, this.color, this.colors, this.opacity = 1 }) { this.name = getIconNames(iconName); } From 1a2e3758519ea7fb290b5e4d8b2cfc105e07e57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E6=98=B1=E9=9C=96?= Date: Fri, 3 Dec 2021 15:58:56 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9E:=20opacity=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=AD=97=E7=AC=A6=E4=B8=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/libs/generateComponent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/generateComponent.ts b/src/libs/generateComponent.ts index 3e2b445..34a30d0 100644 --- a/src/libs/generateComponent.ts +++ b/src/libs/generateComponent.ts @@ -108,7 +108,7 @@ const addAttribute = (domName: string, sub: XmlData['svg']['symbol'][number]['pa template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}="''' + getColor(${counter.colorIndex}, color, colors, '${sub.$[attributeName]}') + '''"`; counter.colorIndex += 1; } else if (attributeName === 'fill-opacity') { - template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}=$opacity`; + template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}="$opacity"`; } else { template += `\n${whitespace(counter.baseIdent + 4)}${attributeName}="${sub.$[attributeName]}"`; } From a398d754e8f92de1bcf548010b158ee58d1704ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E6=B1=82=E5=9C=A3=E5=89=91?= Date: Fri, 30 May 2025 17:46:06 +0800 Subject: [PATCH 3/3] feat: Refactor and implement Flutter IconFont generator - Renamed project to `flutter_iconfont_generator` and updated description in `pubspec.yaml`. - Added iconfont configuration options in `pubspec.yaml` for symbol URL, save directory, icon prefix, default size, and null safety. - Implemented command line tool for generating iconfont code using Dart. - Created a simplified generator for easier usage. - Added comprehensive usage guide and README documentation. - Implemented a builder for integration with `build_runner`. - Developed SVG fetching and parsing logic to extract icons from iconfont.cn. - Added tests for configuration validation and generator functionality. - Created example application demonstrating the usage of generated icons. --- README_DART.md | 147 ++++++++++ USAGE_GUIDE.md | 155 +++++++++++ bin/iconfont_generator.dart | 78 ++++++ bin/simple_generator.dart | 251 +++++++++++++++++ bin/test_all.dart | 92 +++++++ bin/test_config.dart | 43 +++ build.yaml | 15 ++ example/main.dart | 77 ++++++ lib/builder.dart | 67 +++++ lib/iconfont.dart | 12 + lib/src/config.dart | 36 +++ lib/src/fetcher.dart | 40 +++ lib/src/generator.dart | 250 +++++++++++++++++ lib/src/svg_parser.dart | 62 +++++ pubspec.lock | 520 +++++++++++++++++++++++++++++++----- pubspec.yaml | 88 ++---- 16 files changed, 1812 insertions(+), 121 deletions(-) create mode 100644 README_DART.md create mode 100644 USAGE_GUIDE.md create mode 100644 bin/iconfont_generator.dart create mode 100644 bin/simple_generator.dart create mode 100644 bin/test_all.dart create mode 100644 bin/test_config.dart create mode 100644 build.yaml create mode 100644 example/main.dart create mode 100644 lib/builder.dart create mode 100644 lib/iconfont.dart create mode 100644 lib/src/config.dart create mode 100644 lib/src/fetcher.dart create mode 100644 lib/src/generator.dart create mode 100644 lib/src/svg_parser.dart diff --git a/README_DART.md b/README_DART.md new file mode 100644 index 0000000..f40f98c --- /dev/null +++ b/README_DART.md @@ -0,0 +1,147 @@ +# Flutter IconFont CLI - Dart 版本 + +基于 Dart 重构的 iconfont.cn 图标转换工具,使用 build_runner 进行代码生成。 + +## 主要特性 + +- ✅ 纯 Dart 实现,无需 Node.js +- ✅ 通过 `pubspec.yaml` 配置参数 +- ✅ 使用 `build_runner` 进行代码生成 +- ✅ 支持 null safety +- ✅ 支持多色图标 +- ✅ 命令行工具和构建器两种使用方式 + +## 配置说明 + +在 `pubspec.yaml` 中添加配置: + +```yaml +dependencies: + flutter: + sdk: flutter + flutter_svg: ^2.0.0 + http: ^0.13.0 + xml: ^6.0.0 + path: ^1.8.0 + +dev_dependencies: + flutter_test: + sdk: flutter + build_runner: ^2.3.0 + source_gen: ^1.2.0 + analyzer: ^5.0.0 + build: ^2.3.0 + yaml: ^3.1.0 + +# IconFont 配置 +iconfont: + symbol_url: "//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js" # 从 iconfont.cn 获取 + save_dir: "./lib/iconfont" # 输出目录 + trim_icon_prefix: "icon" # 移除图标名前缀 + default_icon_size: 18 # 默认图标大小 + null_safety: true # 是否使用 null safety +``` + +## 使用方法 + +### 方法一:使用 build_runner(推荐) + +1. 添加配置到 `pubspec.yaml` +2. 运行代码生成: + +```bash +# 一次性生成 +dart run build_runner build + +# 监听模式(文件变化时自动重新生成) +dart run build_runner watch + +# 强制重新生成 +dart run build_runner build --delete-conflicting-outputs +``` + +### 方法二:使用命令行工具 + +```bash +# 直接运行生成器 +dart run bin/iconfont_generator.dart + +# 或者安装后使用 +dart pub global activate --source path . +iconfont_generator +``` + +## 生成的代码使用 + +```dart +import 'package:your_app/iconfont/iconfont.dart'; + +// 基本使用 +IconFont(IconNames.home) + +// 指定大小和颜色 +IconFont( + IconNames.user, + size: 24, + color: '#ff0000', +) + +// 多色图标 +IconFont( + IconNames.colorful_icon, + size: 32, + colors: ['#ff0000', '#00ff00', '#0000ff'], +) + +// 使用字符串(动态) +IconFont('home', size: 20) +``` + +## 从 TypeScript 版本迁移 + +1. 删除 `node_modules` 和 `package.json` +2. 更新 `pubspec.yaml` 配置 +3. 运行 `dart pub get` +4. 使用 `dart run build_runner build` 生成图标 + +## 配置参数说明 + +| 参数 | 类型 | 默认值 | 说明 | +|-----|------|--------|------| +| `symbol_url` | String | - | iconfont.cn 提供的 Symbol 链接 | +| `save_dir` | String | "./lib/iconfont" | 生成文件的保存目录 | +| `trim_icon_prefix` | String | "icon" | 要移除的图标名前缀 | +| `default_icon_size` | int | 18 | 默认图标大小 | +| `null_safety` | bool | true | 是否生成 null safety 代码 | + +## 故障排除 + +### 常见问题 + +1. **找不到配置**: 确保 `pubspec.yaml` 中有 `iconfont` 配置节 +2. **网络错误**: 检查 `symbol_url` 是否正确 +3. **生成失败**: 运行 `dart run build_runner clean` 后重试 + +### 调试模式 + +```bash +# 查看详细错误信息 +dart run bin/iconfont_generator.dart --verbose +``` + +## 开发说明 + +项目结构: +``` +lib/ +├── src/ +│ ├── config.dart # 配置模型 +│ ├── fetcher.dart # 网络请求 +│ ├── svg_parser.dart # SVG 解析 +│ └── generator.dart # 代码生成 +├── builder.dart # build_runner 构建器 +└── iconfont.dart # 入口文件 + +bin/ +└── iconfont_generator.dart # 命令行工具 +``` diff --git a/USAGE_GUIDE.md b/USAGE_GUIDE.md new file mode 100644 index 0000000..f5cfca1 --- /dev/null +++ b/USAGE_GUIDE.md @@ -0,0 +1,155 @@ +# Flutter IconFont CLI - Dart 版本使用指南 + +## 快速开始 + +### 1. 配置 pubspec.yaml + +```yaml +dependencies: + flutter: + sdk: flutter + flutter_svg: ^2.0.0 + http: ^0.13.0 + xml: ^6.0.0 + path: ^1.8.0 + +dev_dependencies: + build_runner: ^2.3.0 + yaml: ^3.1.0 + +# IconFont 配置 +iconfont: + symbol_url: "//at.alicdn.com/t/font_xxx.js" # 从 iconfont.cn 复制 + save_dir: "./lib/iconfont" + trim_icon_prefix: "icon" + default_icon_size: 18 + null_safety: true +``` + +### 2. 获取 iconfont.cn 链接 + +1. 登录 [iconfont.cn](https://iconfont.cn) +2. 创建或选择图标项目 +3. 点击"Font class" 或 "Symbol" +4. 复制生成的 JS 链接 (如: `//at.alicdn.com/t/font_xxx.js`) +5. 将链接配置到 `pubspec.yaml` 的 `symbol_url` + +### 3. 生成图标代码 + +```bash +# 安装依赖 +dart pub get + +# 方法一: 使用简化生成器(推荐) +dart run bin/simple_generator.dart + +# 方法二: 使用 build_runner +dart run build_runner build + +# 方法三: 使用完整生成器 +dart run bin/iconfont_generator.dart +``` + +### 4. 使用生成的图标 + +```dart +import 'package:your_app/iconfont/iconfont.dart'; + +// 基本使用 +IconFont(IconNames.home) + +// 设置大小和颜色 +IconFont( + IconNames.user, + size: 24, + color: '#ff0000', +) + +// 多色图标 +IconFont( + IconNames.colorful, + size: 32, + colors: ['#ff0000', '#00ff00', '#0000ff'], +) + +// 动态使用(字符串) +IconFont('home', size: 20) +``` + +## 配置选项 + +| 选项 | 类型 | 默认值 | 说明 | +|-----|------|--------|------| +| `symbol_url` | String | - | iconfont.cn 的 Symbol 或 JS 链接 | +| `save_dir` | String | "./lib/iconfont" | 生成文件保存目录 | +| `trim_icon_prefix` | String | "icon" | 要移除的图标前缀 | +| `default_icon_size` | int | 18 | 默认图标大小 | +| `null_safety` | bool | true | 是否生成 null safety 代码 | + +## 常见问题 + +### Q: 如何更新图标? +A: 重新运行生成命令即可,会自动覆盖旧文件。 + +### Q: 支持多色图标吗? +A: 支持,使用 `colors` 参数传入颜色数组。 + +### Q: 可以动态使用图标吗? +A: 可以,传入字符串形式的图标名称。 + +### Q: 生成失败怎么办? +A: 检查网络连接和 `symbol_url` 是否正确。 + +## 从 TypeScript 版本迁移 + +1. 删除 `node_modules`、`package.json`、`package-lock.json` +2. 按上述步骤配置 `pubspec.yaml` +3. 运行 `dart pub get` +4. 使用 Dart 版本的生成命令 + +## 高级用法 + +### 自定义模板 + +你可以修改 `bin/simple_generator.dart` 中的模板函数来自定义生成的代码。 + +### 批量处理 + +```bash +# 监听文件变化自动生成 +dart run build_runner watch + +# 强制重新生成 +dart run build_runner build --delete-conflicting-outputs +``` + +### 调试模式 + +```bash +dart run bin/simple_generator.dart --verbose +``` + +## 项目结构 + +生成后的项目结构: +``` +lib/ +├── iconfont/ +│ └── iconfont.dart # 生成的图标文件 +└── main.dart +``` + +## 示例项目 + +参考 `example/main.dart` 查看完整的使用示例。 + +## 问题反馈 + +如果遇到问题,请检查: + +1. ✅ `pubspec.yaml` 配置是否正确 +2. ✅ `symbol_url` 是否有效 +3. ✅ 网络连接是否正常 +4. ✅ 依赖是否已安装 (`dart pub get`) + +更多问题请查看项目 README 或提交 Issue。 diff --git a/bin/iconfont_generator.dart b/bin/iconfont_generator.dart new file mode 100644 index 0000000..94067da --- /dev/null +++ b/bin/iconfont_generator.dart @@ -0,0 +1,78 @@ +#!/usr/bin/env dart + +import 'dart:io'; +import 'package:yaml/yaml.dart'; +import '../lib/src/config.dart'; +import '../lib/src/fetcher.dart'; +import '../lib/src/svg_parser.dart'; +import '../lib/src/generator.dart'; + +/// Command line tool for generating iconfont +Future main(List arguments) async { + try { + print('🚀 Flutter IconFont Generator'); + print(''); + + // Read configuration from pubspec.yaml + final pubspecFile = File('pubspec.yaml'); + if (!await pubspecFile.exists()) { + print('❌ Error: pubspec.yaml not found in current directory'); + exit(1); + } + + final pubspecContent = await pubspecFile.readAsString(); + final pubspec = loadYaml(pubspecContent) as Map; + + final iconfontConfig = pubspec['iconfont'] as Map?; + if (iconfontConfig == null) { + print('❌ Error: No iconfont configuration found in pubspec.yaml'); + print(''); + print('Please add iconfont configuration to your pubspec.yaml:'); + print(''); + print('iconfont:'); + print(' symbol_url: "your_iconfont_symbol_url_here"'); + print(' save_dir: "./lib/iconfont"'); + print(' trim_icon_prefix: "icon"'); + print(' default_icon_size: 18'); + print(' null_safety: true'); + exit(1); + } + + final config = + IconFontConfig.fromMap(Map.from(iconfontConfig)); + + if (config.symbolUrl.isEmpty || config.symbolUrl.contains('请参考README.md')) { + print('❌ Error: Please configure a valid symbol_url in pubspec.yaml'); + exit(1); + } + + print('📡 Fetching icons from: ${config.symbolUrl}'); + + // Fetch SVG content + final svgContent = await IconFontFetcher.fetchSvgContent(config.symbolUrl); + + // Parse symbols + final symbols = SvgParser.parseSymbols(svgContent); + + if (symbols.isEmpty) { + print('❌ Warning: No icons found in the SVG content'); + return; + } + + print('✅ Found ${symbols.length} icons'); + + // Generate code + await CodeGenerator.generateIconFont(symbols, config); + + print('🎉 Successfully generated iconfont code!'); + print('📁 Output directory: ${config.saveDir}'); + } catch (e, stackTrace) { + print('❌ Error generating iconfont: $e'); + if (arguments.contains('--verbose')) { + print(''); + print('Stack trace:'); + print(stackTrace); + } + exit(1); + } +} diff --git a/bin/simple_generator.dart b/bin/simple_generator.dart new file mode 100644 index 0000000..bea95f6 --- /dev/null +++ b/bin/simple_generator.dart @@ -0,0 +1,251 @@ +#!/usr/bin/env dart + +import 'dart:io'; +import 'dart:convert'; +import 'package:yaml/yaml.dart'; +import 'package:http/http.dart' as http; + +/// 简化的 IconFont 生成器 +Future main(List arguments) async { + try { + print('🚀 Flutter IconFont Generator (Simple Version)'); + + // 读取配置 + final pubspecContent = await File('pubspec.yaml').readAsString(); + final pubspec = loadYaml(pubspecContent) as Map; + final config = pubspec['iconfont'] as Map?; + + if (config == null) { + print('❌ No iconfont configuration found'); + exit(1); + } + + final symbolUrl = config['symbol_url'] as String; + final saveDir = config['save_dir'] as String? ?? './lib/iconfont'; + final defaultSize = config['default_icon_size'] as int? ?? 18; + final nullSafety = config['null_safety'] as bool? ?? true; + + if (symbolUrl.contains('请参考') || symbolUrl.isEmpty) { + print('❌ Please set a valid symbol_url in pubspec.yaml'); + exit(1); + } + + print('📡 Fetching from: $symbolUrl'); + + // 获取 JS 内容 + String jsUrl = symbolUrl; + if (jsUrl.contains('symbol')) { + jsUrl = jsUrl.replaceAll('symbol', 'js'); + } + if (!jsUrl.startsWith('http')) { + jsUrl = 'https:$jsUrl'; + } + + final response = await http.get(Uri.parse(jsUrl)); + if (response.statusCode != 200) { + print('❌ Failed to fetch: ${response.statusCode}'); + exit(1); + } + + // 简单的图标提取 + final content = response.body; + final symbolMatches = RegExp(r']*id="([^"]*)"[^>]*>(.*?)', dotAll: true) + .allMatches(content); + + if (symbolMatches.isEmpty) { + print('❌ No symbols found'); + exit(1); + } + + print('✅ Found ${symbolMatches.length} icons'); + + // 生成代码 + final enumNames = []; + final cases = StringBuffer(); + final convertCases = StringBuffer(); + + for (final match in symbolMatches) { + final id = match.group(1)!; + final svgContent = match.group(2)!; + + // 处理图标名 + String enumName = id.replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_'); + if (RegExp(r'^\d').hasMatch(enumName)) { + enumName = '_$enumName'; + } + + enumNames.add(enumName); + + // 生成 switch case + cases.writeln(' case IconNames.$enumName:'); + cases.writeln(' svgXml = \'\'\''); + cases.writeln(' '); + cases.writeln(' $svgContent'); + cases.writeln(' \'\'\';'); + cases.writeln(' break;'); + + // 生成字符串转换 + convertCases.writeln(' case \'$enumName\':'); + convertCases.writeln(' return IconNames.$enumName;'); + } + + // 生成文件内容 + final template = nullSafety ? _getNullSafetyTemplate() : _getRegularTemplate(); + final generated = template + .replaceAll('#names#', enumNames.join(', ')) + .replaceAll('#size#', defaultSize.toString()) + .replaceAll('#cases#', cases.toString().trim()) + .replaceAll('#convertCases#', convertCases.toString().trim()); + + // 创建输出目录 + final outputDir = Directory(saveDir); + if (!await outputDir.exists()) { + await outputDir.create(recursive: true); + } + + // 写入文件 + final outputFile = File('$saveDir/iconfont.dart'); + await outputFile.writeAsString(generated); + + print('🎉 Successfully generated ${enumNames.length} icons'); + print('📁 Output: ${outputFile.path}'); + + } catch (e, stackTrace) { + print('❌ Error: $e'); + if (arguments.contains('--verbose')) { + print(stackTrace); + } + exit(1); + } +} + +String _getNullSafetyTemplate() { + return ''' +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +enum IconNames { + #names# +} + +extension IconNamesExtension on IconNames { + String get name => toString().split('.').last; +} + +/// IconFont widget for iconfont.cn icons +class IconFont extends StatelessWidget { + const IconFont( + this.iconName, { + super.key, + this.size = #size#, + this.color, + this.colors, + }); + + final dynamic iconName; + final double size; + final String? color; + final List? colors; + + IconNames get _iconName => _getIconNames(iconName); + + static IconNames _getIconNames(dynamic iconName) { + if (iconName is IconNames) return iconName; + + switch (iconName.toString()) { +#convertCases# + } + return IconNames.values.first; + } + + static String getColor(int index, String? color, List? colors, String defaultColor) { + if (color?.isNotEmpty == true) return color!; + if (colors != null && colors.length > index) return colors[index]; + return defaultColor; + } + + @override + Widget build(BuildContext context) { + final String svgXml; + + switch (_iconName) { +#cases# + default: + svgXml = ''; + } + + return SvgPicture.string( + svgXml, + width: size, + height: size, + ); + } +} +'''; +} + +String _getRegularTemplate() { + return ''' +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +enum IconNames { + #names# +} + +extension IconNamesExtension on IconNames { + String get name => toString().split('.').last; +} + +/// IconFont widget for iconfont.cn icons +class IconFont extends StatelessWidget { + final dynamic iconName; + final double size; + final String color; + final List colors; + + IconFont( + this.iconName, { + Key key, + this.size = #size#, + this.color, + this.colors, + }) : super(key: key); + + IconNames get _iconName => _getIconNames(iconName); + + static IconNames _getIconNames(dynamic iconName) { + if (iconName is IconNames) return iconName; + + switch (iconName.toString()) { +#convertCases# + } + return IconNames.values.first; + } + + static String getColor(int index, String color, List colors, String defaultColor) { + if (color != null && color.isNotEmpty) return color; + if (colors != null && colors.length > index) return colors[index]; + return defaultColor; + } + + @override + Widget build(BuildContext context) { + String svgXml; + + switch (_iconName) { +#cases# + default: + svgXml = ''; + } + + return SvgPicture.string( + svgXml, + width: size, + height: size, + ); + } +} +'''; +} +'''; diff --git a/bin/test_all.dart b/bin/test_all.dart new file mode 100644 index 0000000..7319cdf --- /dev/null +++ b/bin/test_all.dart @@ -0,0 +1,92 @@ +#!/usr/bin/env dart + +import 'dart:io'; + +/// 测试完整的 IconFont 生成流程 +Future main(List arguments) async { + print('🧪 Testing Flutter IconFont Generator...\n'); + + try { + // 1. 检查依赖 + print('1️⃣ Checking dependencies...'); + final pubGetResult = await Process.run('dart', ['pub', 'get']); + if (pubGetResult.exitCode != 0) { + print('❌ dart pub get failed'); + print(pubGetResult.stderr); + exit(1); + } + print('✅ Dependencies installed\n'); + + // 2. 检查配置 + print('2️⃣ Checking configuration...'); + final pubspecFile = File('pubspec.yaml'); + final content = await pubspecFile.readAsString(); + + if (!content.contains('iconfont:')) { + print('❌ No iconfont configuration found'); + exit(1); + } + + if (content.contains('请参考README.md')) { + print('⚠️ Warning: Please update symbol_url in pubspec.yaml'); + } + + print('✅ Configuration found\n'); + + // 3. 测试简化生成器 + print('3️⃣ Testing simple generator...'); + + // 创建备份配置用于测试 + final testConfig = ''' +iconfont: + symbol_url: "//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js" + save_dir: "./test_output" + trim_icon_prefix: "icon" + default_icon_size: 18 + null_safety: true +'''; + + // 临时修改配置进行测试 + final originalContent = content; + final testContent = content.replaceAll( + RegExp(r'iconfont:.*?null_safety: true', dotAll: true), + testConfig.trim()); + + await pubspecFile.writeAsString(testContent); + + try { + final generatorResult = await Process.run( + 'dart', + ['run', 'bin/simple_generator.dart'], + workingDirectory: Directory.current.path, + ); + + if (generatorResult.exitCode == 0) { + print('✅ Simple generator test passed'); + print(generatorResult.stdout); + } else { + print('⚠️ Simple generator test failed (expected if no internet)'); + print(generatorResult.stderr); + } + } finally { + // 恢复原始配置 + await pubspecFile.writeAsString(originalContent); + + // 清理测试输出 + final testDir = Directory('./test_output'); + if (await testDir.exists()) { + await testDir.delete(recursive: true); + } + } + + print('\n4️⃣ All tests completed!'); + print('\n📋 Next steps:'); + print(' 1. 在 pubspec.yaml 中配置正确的 symbol_url'); + print(' 2. 运行: dart run bin/simple_generator.dart'); + print(' 3. 在代码中导入并使用生成的图标'); + print('\n📚 参考文档: USAGE_GUIDE.md'); + } catch (e) { + print('❌ Test failed: $e'); + exit(1); + } +} diff --git a/bin/test_config.dart b/bin/test_config.dart new file mode 100644 index 0000000..1e7ba0d --- /dev/null +++ b/bin/test_config.dart @@ -0,0 +1,43 @@ +#!/usr/bin/env dart + +import 'dart:io'; +import 'package:yaml/yaml.dart'; + +// 简化的测试,不使用复杂的生成器 +Future main(List arguments) async { + try { + print('🚀 Flutter IconFont Generator Test'); + + // 读取配置 + final pubspecFile = File('pubspec.yaml'); + if (!await pubspecFile.exists()) { + print('❌ pubspec.yaml not found'); + exit(1); + } + + final content = await pubspecFile.readAsString(); + final pubspec = loadYaml(content) as Map; + final iconfontConfig = pubspec['iconfont'] as Map?; + + if (iconfontConfig == null) { + print('❌ No iconfont config found'); + exit(1); + } + + print('✅ Configuration loaded:'); + print(' Symbol URL: ${iconfontConfig['symbol_url']}'); + print(' Save Dir: ${iconfontConfig['save_dir']}'); + print(' Prefix: ${iconfontConfig['trim_icon_prefix']}'); + print(' Size: ${iconfontConfig['default_icon_size']}'); + print(' Null Safety: ${iconfontConfig['null_safety']}'); + + print('\n🎉 Test completed successfully!'); + print('📝 Next steps:'); + print(' 1. 更新 symbol_url 为真实的 iconfont 链接'); + print(' 2. 运行: dart run bin/iconfont_generator.dart'); + print(' 3. 或使用: dart run build_runner build'); + } catch (e) { + print('❌ Error: $e'); + exit(1); + } +} diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..df7f17c --- /dev/null +++ b/build.yaml @@ -0,0 +1,15 @@ +targets: + $default: + builders: + flutter_iconfont_generator:iconfont_builder: + enabled: true + generate_for: + - lib/iconfont.dart + +builders: + iconfont_builder: + import: "package:flutter_iconfont_generator/builder.dart" + builder_factories: ["iconFontBuilder"] + build_extensions: {".dart": [".g.dart"]} + auto_apply: dependents + build_to: source diff --git a/example/main.dart b/example/main.dart new file mode 100644 index 0000000..122764e --- /dev/null +++ b/example/main.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'iconfont/iconfont.dart'; + +void main() { + runApp(MyApp()); +} + +class MyApp extends StatelessWidget { + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'IconFont Demo', + home: IconFontDemo(), + ); + } +} + +class IconFontDemo extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('IconFont Demo'), + ), + body: Padding( + padding: EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text('基本使用:', style: Theme.of(context).textTheme.headlineSmall), + SizedBox(height: 16), + Row( + children: [ + IconFont(IconNames.values.first, size: 24), + SizedBox(width: 16), + IconFont(IconNames.values.first, size: 32, color: '#ff0000'), + SizedBox(width: 16), + IconFont(IconNames.values.first, size: 40, color: '#00ff00'), + ], + ), + SizedBox(height: 32), + Text('所有图标:', style: Theme.of(context).textTheme.headlineSmall), + SizedBox(height: 16), + Expanded( + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + childAspectRatio: 1, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + ), + itemCount: IconNames.values.length, + itemBuilder: (context, index) { + final iconName = IconNames.values[index]; + return Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconFont(iconName, size: 32), + SizedBox(height: 8), + Text( + iconName.name, + style: TextStyle(fontSize: 12), + textAlign: TextAlign.center, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ], + ); + }, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/builder.dart b/lib/builder.dart new file mode 100644 index 0000000..73453d0 --- /dev/null +++ b/lib/builder.dart @@ -0,0 +1,67 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:build/build.dart'; +import 'package:yaml/yaml.dart'; +import 'src/config.dart'; +import 'src/fetcher.dart'; +import 'src/svg_parser.dart'; +import 'src/generator.dart'; + +/// Builder for generating iconfont code +class IconFontBuilder implements Builder { + @override + Map> get buildExtensions => { + '.dart': ['.g.dart'], + }; + + @override + Future build(BuildStep buildStep) async { + try { + // Read pubspec.yaml to get iconfont configuration + final pubspecContent = await File('pubspec.yaml').readAsString(); + final pubspec = loadYaml(pubspecContent) as Map; + + final iconfontConfig = pubspec['iconfont'] as Map?; + if (iconfontConfig == null) { + log.warning('No iconfont configuration found in pubspec.yaml'); + return; + } + + final config = + IconFontConfig.fromMap(Map.from(iconfontConfig)); + + if (config.symbolUrl.isEmpty || + config.symbolUrl.contains('请参考README.md')) { + log.warning('Please configure a valid symbol_url in pubspec.yaml'); + return; + } + + log.info('Fetching icons from: ${config.symbolUrl}'); + + // Fetch SVG content + final svgContent = + await IconFontFetcher.fetchSvgContent(config.symbolUrl); + + // Parse symbols + final symbols = SvgParser.parseSymbols(svgContent); + + if (symbols.isEmpty) { + log.warning('No icons found in the SVG content'); + return; + } + + log.info('Found ${symbols.length} icons'); + + // Generate code + await CodeGenerator.generateIconFont(symbols, config); + + log.info('Successfully generated iconfont code'); + } catch (e, stackTrace) { + log.severe('Error generating iconfont: $e', e, stackTrace); + rethrow; + } + } +} + +/// Builder factory function +Builder iconFontBuilder(BuilderOptions options) => IconFontBuilder(); diff --git a/lib/iconfont.dart b/lib/iconfont.dart new file mode 100644 index 0000000..bdb068c --- /dev/null +++ b/lib/iconfont.dart @@ -0,0 +1,12 @@ +/// A generator entry point file for build_runner +/// This file is used to trigger the iconfont generation process +/// +/// To generate icons, run: +/// dart run build_runner build +/// +/// Or for watch mode: +/// dart run build_runner watch + +// This file intentionally left mostly empty +// The actual generation is handled by the builder +const String _generatorMarker = 'iconfont_generator'; diff --git a/lib/src/config.dart b/lib/src/config.dart new file mode 100644 index 0000000..655266f --- /dev/null +++ b/lib/src/config.dart @@ -0,0 +1,36 @@ +/// Configuration model for iconfont generation +class IconFontConfig { + final String symbolUrl; + final String saveDir; + final String trimIconPrefix; + final int defaultIconSize; + final bool nullSafety; + + const IconFontConfig({ + required this.symbolUrl, + required this.saveDir, + required this.trimIconPrefix, + required this.defaultIconSize, + required this.nullSafety, + }); + + factory IconFontConfig.fromMap(Map map) { + return IconFontConfig( + symbolUrl: map['symbol_url'] ?? '', + saveDir: map['save_dir'] ?? './lib/iconfont', + trimIconPrefix: map['trim_icon_prefix'] ?? 'icon', + defaultIconSize: map['default_icon_size'] ?? 18, + nullSafety: map['null_safety'] ?? true, + ); + } + + Map toMap() { + return { + 'symbol_url': symbolUrl, + 'save_dir': saveDir, + 'trim_icon_prefix': trimIconPrefix, + 'default_icon_size': defaultIconSize, + 'null_safety': nullSafety, + }; + } +} diff --git a/lib/src/fetcher.dart b/lib/src/fetcher.dart new file mode 100644 index 0000000..fd8c9a5 --- /dev/null +++ b/lib/src/fetcher.dart @@ -0,0 +1,40 @@ +import 'dart:convert'; +import 'package:http/http.dart' as http; + +/// Fetches SVG content from iconfont.cn +class IconFontFetcher { + static Future fetchSvgContent(String symbolUrl) async { + try { + // Convert symbol URL to JS URL if needed + String jsUrl = symbolUrl; + if (!jsUrl.startsWith('http')) { + throw ArgumentError('Invalid symbol URL: $symbolUrl'); + } + + // If it's a symbol URL, convert to JS URL + if (jsUrl.contains('symbol')) { + jsUrl = jsUrl.replaceAll('symbol', 'js'); + } + + final response = await http.get(Uri.parse(jsUrl)); + + if (response.statusCode != 200) { + throw Exception( + 'Failed to fetch iconfont data: ${response.statusCode}'); + } + + final content = response.body; + + // Extract SVG content from JS + final svgMatch = + RegExp(r']*>(.*?)', dotAll: true).firstMatch(content); + if (svgMatch == null) { + throw Exception('No SVG content found in response'); + } + + return '${svgMatch.group(1)}'; + } catch (e) { + throw Exception('Error fetching iconfont data: $e'); + } + } +} diff --git a/lib/src/generator.dart b/lib/src/generator.dart new file mode 100644 index 0000000..06d0611 --- /dev/null +++ b/lib/src/generator.dart @@ -0,0 +1,250 @@ +import 'dart:io'; +import 'package:path/path.dart' as path; +import 'config.dart'; +import 'svg_parser.dart'; + +/// Generates Flutter icon font code +class CodeGenerator { + static String _toCamelCase(String input) { + if (input.isEmpty) return input; + + // Convert to snake_case first + String snakeCase = input + .replaceAllMapped(RegExp(r'[A-Z]'), (match) => '_${match.group(0)!.toLowerCase()}') + .replaceAll(RegExp(r'[^a-zA-Z0-9_]'), '_') + .replaceAll(RegExp(r'_+'), '_') + .toLowerCase(); + + // Remove leading/trailing underscores + snakeCase = snakeCase.replaceAll(RegExp(r'^_+|_+$'), ''); + + // If starts with number, add underscore + if (RegExp(r'^\d').hasMatch(snakeCase)) { + snakeCase = '_$snakeCase'; + } + + return snakeCase; + } + + static String _generateEnumCase(SvgSymbol symbol, IconFontConfig config) { + String iconId = symbol.id; + + // Remove prefix if specified + if (config.trimIconPrefix.isNotEmpty) { + final prefixRegex = RegExp('^${config.trimIconPrefix}(.+?)\$'); + iconId = iconId.replaceFirst(prefixRegex, '\$1'); + } + + return _toCamelCase(iconId); + } + + static String _generateSvgCase(SvgSymbol symbol, IconFontConfig config) { + final buffer = StringBuffer(); + buffer.writeln(' '); + + for (int i = 0; i < symbol.paths.length; i++) { + final path = symbol.paths[i]; + buffer.write(' '); + } + + buffer.write(' '); + return buffer.toString(); + } + + static Future generateIconFont(List symbols, IconFontConfig config) async { + final names = []; + final cases = StringBuffer(); + final convertCases = StringBuffer(); + + // Generate enum names and cases + for (final symbol in symbols) { + final enumName = _generateEnumCase(symbol, config); + names.add(enumName); + + // Generate switch case for build method + cases.writeln(' case IconNames.$enumName:'); + cases.writeln(' svgXml = \'\'\''); + cases.write(_generateSvgCase(symbol, config)); + cases.writeln('\'\'\';'); + cases.writeln(' break;'); + + // Generate string to enum conversion case + convertCases.writeln(' case \'$enumName\':'); + convertCases.writeln(' return IconNames.$enumName;'); + } + + // Generate the Flutter code + final template = _getTemplate(config); + String iconFile = template + .replaceAll('#names#', names.join(', ')) + .replaceAll('#size#', config.defaultIconSize.toString()) + .replaceAll('#cases#', cases.toString().trimRight()) + .replaceAll('#convertCases#', convertCases.toString().trimRight()); + + // Ensure save directory exists + final saveDir = Directory(config.saveDir); + if (!await saveDir.exists()) { + await saveDir.create(recursive: true); + } + + // Clean existing files + await for (final file in saveDir.list()) { + if (file is File && file.path.endsWith('.dart')) { + await file.delete(); + } + } + + // Write the generated file + final outputFile = File(path.join(config.saveDir, 'iconfont.dart')); + await outputFile.writeAsString(iconFile); + + print('Generated ${symbols.length} icons to ${outputFile.path}'); + } + + static String _getTemplate(IconFontConfig config) { + if (config.nullSafety) { + return ''' +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +enum IconNames { + #names# +} + +extension IconNamesExtension on IconNames { + String get name => toString().split('.').last; +} + +/// IconFont widget for iconfont.cn icons +class IconFont extends StatelessWidget { + const IconFont( + this.iconName, { + super.key, + this.size = #size#, + this.color, + this.colors, + }); + + final dynamic iconName; + final double size; + final String? color; + final List? colors; + + IconNames get _iconName => _getIconNames(iconName); + + static IconNames _getIconNames(dynamic iconName) { + if (iconName is IconNames) return iconName; + + switch (iconName.toString()) { +#convertCases# + } + return IconNames.values.first; + } + + static String getColor(int index, String? color, List? colors, String defaultColor) { + if (color?.isNotEmpty == true) return color!; + if (colors != null && colors.length > index) return colors[index]; + return defaultColor; + } + + @override + Widget build(BuildContext context) { + final String svgXml; + + switch (_iconName) { +#cases# + default: + svgXml = ''; + } + + return SvgPicture.string( + svgXml, + width: size, + height: size, + ); + } +} +'''; + } else { + return ''' +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +enum IconNames { + #names# +} + +extension IconNamesExtension on IconNames { + String get name => toString().split('.').last; +} + +/// IconFont widget for iconfont.cn icons +class IconFont extends StatelessWidget { + final dynamic iconName; + final double size; + final String color; + final List colors; + + IconFont( + this.iconName, { + Key key, + this.size = #size#, + this.color, + this.colors, + }) : super(key: key); + + IconNames get _iconName => _getIconNames(iconName); + + static IconNames _getIconNames(dynamic iconName) { + if (iconName is IconNames) return iconName; + + switch (iconName.toString()) { +#convertCases# + } + return IconNames.values.first; + } + + static String getColor(int index, String color, List colors, String defaultColor) { + if (color != null && color.isNotEmpty) return color; + if (colors != null && colors.length > index) return colors[index]; + return defaultColor; + } + + @override + Widget build(BuildContext context) { + String svgXml; + + switch (_iconName) { +#cases# + default: + svgXml = ''; + } + + return SvgPicture.string( + svgXml, + width: size, + height: size, + ); + } +} +'''; + } + } +} diff --git a/lib/src/svg_parser.dart b/lib/src/svg_parser.dart new file mode 100644 index 0000000..1b7381a --- /dev/null +++ b/lib/src/svg_parser.dart @@ -0,0 +1,62 @@ +import 'package:xml/xml.dart'; + +/// Represents an SVG symbol from iconfont +class SvgSymbol { + final String id; + final String viewBox; + final List paths; + + SvgSymbol({ + required this.id, + required this.viewBox, + required this.paths, + }); +} + +/// Represents an SVG path element +class SvgPath { + final String d; + final String? fill; + final Map attributes; + + SvgPath({ + required this.d, + this.fill, + required this.attributes, + }); +} + +/// Parser for iconfont SVG data +class SvgParser { + static List parseSymbols(String svgContent) { + final document = XmlDocument.parse(svgContent); + final symbols = document.findAllElements('symbol'); + + return symbols.map((symbol) { + final id = symbol.getAttribute('id') ?? ''; + final viewBox = symbol.getAttribute('viewBox') ?? '0 0 1024 1024'; + + final paths = symbol.findAllElements('path').map((path) { + final d = path.getAttribute('d') ?? ''; + final fill = path.getAttribute('fill'); + + final attributes = {}; + for (final attr in path.attributes) { + attributes[attr.name.local] = attr.value; + } + + return SvgPath( + d: d, + fill: fill, + attributes: attributes, + ); + }).toList(); + + return SvgSymbol( + id: id, + viewBox: viewBox, + paths: paths, + ); + }).toList(); + } +} diff --git a/pubspec.lock b/pubspec.lock index 4ea8127..84141bd 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,62 +1,198 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a + url: "https://pub.flutter-io.cn" + source: hosted + version: "61.0.0" + analyzer: + dependency: "direct dev" + description: + name: analyzer + sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562 + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.13.0" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.7.0" async: dependency: transitive description: name: async - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.5.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.1" + build: + dependency: "direct dev" + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.9" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + url: "https://pub.flutter-io.cn" + source: hosted + version: "8.10.1" characters: dependency: transitive description: name: characters - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" - charcode: + version: "1.3.0" + checked_yaml: dependency: transitive description: - name: charcode - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "2.0.3" clock: dependency: transitive description: name: clock - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.10.0" collection: dependency: transitive description: name: collection - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.flutter-io.cn" source: hosted - version: "1.15.0" - cupertino_icons: - dependency: "direct main" + version: "1.18.0" + convert: + dependency: transitive description: - name: cupertino_icons - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.2" + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "1efa911ca7086affd35f463ca2fc1799584fb6aa89883cf0af8e3664d6a02d55" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.2" fake_async: dependency: transitive description: name: fake_async - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.3.1" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -66,124 +202,388 @@ packages: dependency: "direct main" description: name: flutter_svg - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + url: "https://pub.flutter-io.cn" source: hosted - version: "0.21.0-nullsafety.0" + version: "2.0.9" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.1" + http: + dependency: "direct main" + description: + name: http + sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.13.6" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.2" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: name: matcher - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.10" + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.0" meta: dependency: transitive description: name: meta - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" - path: + version: "1.11.0" + mime: dependency: transitive description: - name: path - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + name: mime + sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0" - path_drawing: + version: "1.0.6" + package_config: dependency: transitive description: - name: path_drawing - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" + path: + dependency: "direct main" + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0-nullsafety.0" + version: "1.9.0" path_parsing: dependency: transitive description: name: path_parsing - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0-nullsafety.0" + version: "1.1.0" petitparser: dependency: transitive description: name: petitparser - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.2" + version: "6.0.2" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.0" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" + source_gen: + dependency: "direct dev" + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.0" source_span: dependency: transitive description: name: source_span - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: name: string_scanner - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.19" + version: "0.6.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.1" typed_data: dependency: transitive description: name: typed_data - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" + version: "1.3.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.10+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.10+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.10+1" vector_math: dependency: transitive description: name: vector_math - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" - xml: + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.flutter-io.cn" + source: hosted + version: "13.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + web: dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.5.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.5" + xml: + dependency: "direct main" description: name: xml - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub" + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.5.0" + yaml: + dependency: "direct dev" + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.flutter-io.cn" source: hosted - version: "5.0.2" + version: "3.1.2" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.24.0-7.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.7.0-0" diff --git a/pubspec.yaml b/pubspec.yaml index d5318ff..46a60f8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,76 +1,42 @@ -name: flutter_demo -description: A new Flutter project. +name: flutter_iconfont_generator +description: A Dart/Flutter code generator for iconfont.cn icons. -# The following line prevents the package from being accidentally published to -# pub.dev using `pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: 'none' -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 +version: 2.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.17.0 <4.0.0" dependencies: flutter: sdk: flutter - flutter_svg: ^0.21.0-nullsafety.0 - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.0 + flutter_svg: ^2.0.0 + http: ^0.13.0 + xml: ^6.0.0 + path: ^1.8.0 dev_dependencies: flutter_test: sdk: flutter - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. + build_runner: ^2.3.0 + source_gen: ^1.2.0 + analyzer: ^5.0.0 + build: ^2.3.0 + yaml: ^3.1.0 + +# Iconfont configuration +iconfont: + symbol_url: "//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js" + save_dir: "./lib/iconfont" + trim_icon_prefix: "icon" + default_icon_size: 18 + null_safety: true + +# Flutter configuration flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. uses-material-design: true - # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages +# Executable scripts +executables: + iconfont_generator: iconfont_generator