news 2026/10/20 1:46:55

flutter_flutter 模板图片资产管理指南:基于 flutter_template_images 的双仓库工作流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
flutter_flutter 模板图片资产管理指南:基于 flutter_template_images 的双仓库工作流
  • 跨平台
  • 前端
  • 移动开发
  • OpenHarmony

【免费下载链接】flutter_flutter

本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。

项目地址:https://gitcode.com/CPF-Flutter/flutter_flutter
点击查看免费下载

本文面向 flutter_flutter(Flutter 的 OpenHarmony 适配版本)的贡献者与二次开发者,系统讲解flutter工具模板中图片资源的维护机制:为什么图片不能直接提交进主仓库、flutter_template_imagesPub 包在其中扮演的角色、以及新增/更新模板图片时需要在flutter/packages与flutter/flutter两个仓库分别执行的完整操作步骤。读完本文,你将掌握.img.tmpl占位符与template_manifest.json清单的配合方式,并能独立完成一次模板图片的发布与回滚。

为什么模板图片不直接入库

flutter工具在生成新项目(flutter create)时,会向目标工程写入大量模板文件,其中包含各平台的图标、启动图等二进制资源(如 iOS 的AppIcon、macOS 的应用图标、Windows 的app_icon.ico、Web 的 maskable icon 等)。这些图片资源并没有直接存放在 flutter_flutter 主仓库中,而是统一维护在flutter_template_images这个 Pub 包内。

这一设计有两个直接原因:

  1. Presubmit 检查禁止二进制文件入库。主仓库的静态检查verifyNoBinaries(见 dev/bots/analyze.dart)会逐个读取 Git 跟踪文件并以 UTF-8 解码,凡无法解码为合法 UTF-8 的内容都被判定为二进制而报错。该检查的错误提示中明确写着:All files in this repository must be UTF-8. In particular, images and other binaries must not be checked into this repository,并直接引用本指南作为解决方案。
  2. 仓库体积与升级成本。二进制文件会显著增大仓库体积,且每次更新都会产生巨大的 diff。flutter_flutter 仓库会随版本被广大开发者同步,执行flutter upgrade时需要对整个 Git 历史做增量同步,体积膨胀会直接拖慢所有人的升级体验。而从 Pub 拉取一个包只需下载对应版本内容,不涉及 Git 历史的全量同步,明显更轻量。

从本仓库的实际结构也能印证这一点:以 packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset 为例,目录下只有Contents.json是真实文件,而Icon-App-20x20@1x.png.img.tmpl、Icon-App-1024x1024@1x.png.img.tmpl等所有图标都只是零字节占位符,真正的内容在flutter_template_images包中。

双仓库工作流总览

新增或更新模板图片需要同时提交两个补丁(patch),分别落在两个仓库:

  • flutter/packages 仓库:修改flutter_template_images包,把真实图片文件放入其templates目录,并发布新版本到 Pub。
  • flutter/flutter 仓库(即本仓库):把依赖版本提升到新发布的版本,同步添加占位符与清单条目,让flutter工具在生成项目时能正确解析到新图片。

flutter_template_images包内的templates目录布局与 flutter_flutter 仓库中flutter_tools包下的 templates 目录完全一致:先向flutter_template_images提交真实图片,再回滚进主仓库,保证两处路径一一对应。回滚时,flutter_tools包中的占位文件只是同名零字节文件(文件名含.img.tmpl后缀),真实二进制仍留在 Pub 包里。

第一步:向 flutter_template_images 添加资源

1. 分叉 flutter/packages 仓库

在 GitHub 上 fork flutter/packages 仓库(若未配置 GitHub 亦可参考该仓库的本地镜像流程),在本地检出后进入packages/flutter_template_images目录操作。

2. 按目标路径放置图片

将图片放到templates目录下与 flutter_flutter 主仓库对应的相对路径。命名规则有三点要求:

  • 文件名必须与flutter工具生成项目后的最终文件名完全一致,因为模板渲染时不会做任何改名;
  • 不要包含.img.tmpl后缀——该后缀只出现在主仓库的占位符中,真实图片文件用的是最终名,例如主仓库占位符是app_icon.ico.img.tmpl,包内真实文件就叫app_icon.ico;
  • 目录层级要与flutter_tools/templates严格对齐,例如 iOS 图标要放在templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/下。

3. 升级 pubspec.yaml 版本号

编辑该包的pubspec.yaml,版本号为major.minor.patch三段式,规则是:

  • 新增图片资源:递增 minor 版本(如5.0.0→5.1.0);
  • 仅更新已有图片:递增 patch 版本(如5.0.0→5.0.1)。

这个规则与本仓库中flutter_template_images的 pin 位置相互印证:主仓库pubspec.yaml中声明flutter_template_images: 5.0.0(见 packages/flutter_tools/pubspec.yaml),同时手工 pin 集中在kManuallyPinnedDependencies表中,注释写明Must always exactly match flutter_tools template(见 packages/flutter_tools/lib/src/update_packages_pins.dart)。

4. 更新 CHANGELOG.md

在包的CHANGELOG.md顶部为本次版本新增一个标题条目,并添加一行描述变更内容。格式遵循 docs/ecosystem/contributing/README.md#changelog-style 的规范(该目录页同时说明了 packages 仓库采用"持续发布"模型、绝大多数发布型变更都必须更新版本号与 CHANGELOG 的纪律)。

5. 提交并发送 Pull Request

提交改动(建议仅包含图片、版本号、CHANGELOG 三类文件),推送后发送 PR。合入后,post-submit 作业会自动把新版本发布到 Pub,无需手动执行发布命令。你可以在flutter_template_images包页面(Pub 上的Versions标签页)确认新版本是否已成功发布——只有发布成功后,主仓库才能依赖到它。

第二步:将变更回滚进 flutter_flutter 主仓库

新版本在 Pub 发布后,开始在 flutter_flutter 仓库中回滚。

1. 分叉 flutter/flutter 仓库并更新依赖 pin

fork flutter_flutter 仓库后,需要修改依赖版本声明。原指南指向update_packages.dart,而在当前仓库中,手工固定(pin)的依赖统一集中在 packages/flutter_tools/lib/src/update_packages_pins.dart 的kManuallyPinnedDependencies表中:把flutter_template_images的条目从旧版本改为新版本。同时 packages/flutter_tools/pubspec.yaml 中的依赖声明也会随之更新。

需要特别强调的是,该表中的 pin必须是精确版本而非版本范围(源码注释明确指出 ranges 会使 CI 随机失败并可能让用户无法升级 Flutter),因此这里必须写入与包版本完全一致的精确版本号。

2. 运行依赖更新命令

在仓库根目录执行:

flutter update-packages --force-upgrade

该命令会基于kManuallyPinnedDependencies重新解析并锁定额外依赖。从 packages/flutter_tools/lib/src/commands/update_packages.dart 的源码可以看到,--force-upgrade是UpdatePackagesCommand的核心旗标之一;文件头部的注释还说明,Pub 包的例行升级由flutter-pub-roller-bot通过flutter update-packages --force-upgrade自动完成,人工执行时同样走这条命令。

3. 添加零字节占位文件

对于你在flutter_template_images中新增的每一个图片文件,都在flutter_tools包的 templates 目录的对应位置放一个零字节文件,文件名加上.img.tmpl后缀。例如新增 iOS 启动图,就在templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/下创建LaunchImage.png.img.tmpl。

零字节占位的好处是:不携带任何二进制内容,能通过 UTF-8 检查;模板引擎看到.img.tmpl后缀时也不会对其做 mustache 渲染,而是从图片包中复制真实文件覆盖它。

4. 更新 template_manifest.json

编辑 packages/flutter_tools/templates/template_manifest.json,在files数组中为每个新增文件添加一条记录,路径必须包含.img.tmpl后缀。该清单文件的_comment字段说明了对应关系:Files ending in .img.tmpl correspond to files checked in to the flutter_template_images package ... located at the same path, excluding the .img.tmpl suffix.

清单的作用在 packages/flutter_tools/lib/src/commands/create_base.dart 中体现:_computeTemplateManifest()读取该 JSON,把所有files条目解析为绝对路径 URI 集合;随后 packages/flutter_tools/lib/src/template.dart 在遍历模板目录时,会跳过清单中不存在的文件,从而清理掉flutter_tools目录里的过期文件。这保证了"清单驱动模板"的一致性。

5. 测试运行并提交 PR

提交改动后,先在本仓库内实际执行一次模板生成做冒烟测试,确认新图片能被flutter工具正确产出到生成的项目中。验证无误后再发送 PR。若 PR 中缺少占位符或清单条目,CI 会拦截:集成测试 packages/flutter_tools/test/integration.shard/template_manifest_test.dart 会递归扫描templates目录中的所有文件,并与template_manifest.json声明的清单求差集,任何"存在但未声明"的文件都会导致manifest and template directory should be in-sync断言失败。

模板引擎如何消费图片资源

理解.img.tmpl的语义对排查问题很有帮助。模板渲染的核心实现在 packages/flutter_tools/lib/src/template.dart,其类注释定义了整套后缀约定:

后缀含义
.tmpl需要 mustache 渲染后写出
.copy.tmpl无需渲染,直接按原样复制
.img.tmpl从flutter_template_images包复制对应图片
.test.tmpl仅当启用--implementation-tests时才包含

在渲染流程(Template.render,见 template.dart)中,凡源文件以.img.tmpl结尾,引擎会遍历imageSourceDirectories,找到去掉.img.tmpl后缀后的相对路径对应的真实图片文件,直接copySync到目标位置;若找不到则抛出Image File not found的 ToolExit。目标文件路径在写出前会把.tmpl、.copy.tmpl、.img.tmpl、.test.tmpl后缀全部剥掉(见 template.dart),所以最终工程里出现的是干净的真实文件名。

图片目录的定位逻辑在TemplatePathProvider.imageDirectory(见 template.dart):它读取flutter_tools/.dart_tool/package_config.json,按包名flutter_template_images找到packageUriRoot,取其父目录下的templates目录作为图片根目录。换言之,图片解析完全依赖 Dart 包配置——这也解释了为什么主仓库必须先升级依赖并重新执行update-packages,否则新图片根本无法被解析。同样的思路也出现在 iOS 打包逻辑中:IOSApp注释写明"Template asset's images are in flutter_template_images package"(见 packages/flutter_tools/lib/src/ios/application_package.dart)。

本仓库的适配现状与验证线索

作为 OpenHarmony 适配版本,flutter_flutter 保留了整套图片资产管理机制,同时扩展了 OpenHarmony 模板。可以从以下几处快速验证当前状态:

  • 依赖 pin:flutter_template_images: 5.0.0同时出现在 pubspec.yaml 与 update_packages_pins.dart 中,且注释强制要求与模板严格同步。
  • 占位符分布:find可列出 packages/flutter_tools/templates 下全部.img.tmpl占位符,覆盖 iOS(Icon-App-*、LaunchImage*)、macOS(app_icon_*)、Windows(app_icon.ico.img.tmpl)、Web(Icon-maskable-*.png.img.tmpl)等平台。
  • 清单文件:template_manifest.json 中templates/app/ohos.tmpl/...、templates/module/ohos/...、templates/plugin/ohos.tmpl/...等 OpenHarmony 模板条目已被纳入files数组,与适配版模板共存。
  • 渲染过滤:在 template.dart 的renderPath中,ohos与android、ios、web、linux、macos、windows并列,只有创建 OpenHarmony 工程(--platforms=ohos场景)时ohos.tmpl目录下的内容才会被展开,说明 OpenHarmony 模板已深度融入同一套模板管线。

从源码结构还可以看到,OpenHarmony 模板目录(如templates/app/ohos.tmpl/AppScope/resources/base/media/app_icon.png、entry/src/main/resources/base/media/icon.png)当前以真实 PNG 文件直接放在templates目录下,并由清单直接列出(未采用.img.tmpl占位方式),与 iOS/macOS 等平台的占位符策略有所不同——这是适配版当前的实际情况,后续若要统一管理 OH 图标资源,可参照本文工作流迁移到flutter_template_images。

小结

模板图片资产的维护是一条清晰的"包内放真图、仓库放占位"的双仓库流水线:

  1. 在flutter_template_images(flutter/packages)按最终文件名放置真实图片,按"新增 minor / 更新 patch"规则升级版本,更新 CHANGELOG,合入 PR 后由 post-submit 自动发布到 Pub;
  2. 在 flutter_flutter 主仓库更新精确版本 pin(集中在kManuallyPinnedDependencies),执行flutter update-packages --force-upgrade,为每张图片添加同名零字节.img.tmpl占位符,并在 template_manifest.json 中登记清单条目;
  3. 依赖template_manifest_test.dart与verifyNoBinaries等 CI 检查兜底,确保清单与目录同步、仓库不混入二进制。

掌握这套流程后,无论是为 flutter_flutter 的 OpenHarmony 模板补充图标,还是为既有平台模板替换应用图标,都能以最小的仓库体积代价、最快地让flutter create生成出带新资源的工程。

  • 跨平台
  • 前端
  • 移动开发
  • OpenHarmony

【免费下载链接】flutter_flutter

本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。

项目地址:https://gitcode.com/CPF-Flutter/flutter_flutter
点击查看免费下载

相关推荐

上一篇:Wireshark自动化测试终极指南:环境变量配置与CI集成完整教程
下一篇:Metalsmith环境变量配置:开发/生产环境无缝切换方案

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 1:45:39

ESP32-C3中文数据手册实战指南:从管脚定义到低功耗设计的避坑要点

简介:这是一份面向嵌入式开发工程师、物联网产品设计者及电子相关专业学生的 ESP32-C3 芯片中文数据手册,用于快速掌握该芯片的硬件规格、外设资源与低功耗设计要点。资源包内含 1 个 PDF 文件,大小约 1.06MB,即乐鑫官方技术规格书…

作者头像 李华
网站建设 2026/10/20 1:46:18

数据结构面试实战:从手写代码到系统级工程权衡

1. 这不是背题手册,而是一份数据结构面试实战手记“数据结构”这四个字在程序员面试里,从来就不是一张知识清单,而是一面照妖镜——照出你到底有没有真正用过链表写过缓存淘汰逻辑,有没有在调试红黑树插入失败时盯着旋转条件熬过三…

作者头像 李华
网站建设 2026/10/19 5:26:00

浏览器端神经网络训练:WebGL矩阵乘法与GPU加速原理

1. 浏览器里跑神经网络,这件事到底靠不靠谱第一次跟朋友说“我要在浏览器里训练一个神经网络”,对方的反应基本都是一样的:你疯了?浏览器那点性能,跑个复杂点的CSS动画都掉帧,还训练模型?说实话…

作者头像 李华
网站建设 2026/10/18 13:42:52

C++20编译期字符串处理:从fixed_string到静态类型映射的工程实践

最近在重构一个内部工具库时,我遇到了一个特别拧巴的需求组合:日志系统希望自动带上调用点的完整函数签名,序列化模块想在编译期就校验字段名有没有写错,反射注册表想通过一个字符串反查类型。这三个需求凑到一起,就逼…

作者头像 李华
网站建设 2026/10/19 11:58:07

浏览器端机器学习训练实战:基于WebGL的神经网络实现与优化

1. 浏览器端机器学习训练到底在做什么1.1 从“打开网页就能训练模型”说起浏览器端机器学习训练,说白了就是把原本跑在服务器或者本地Python环境里的神经网络训练过程,整个搬到网页里完成。你打开一个页面,不用装CUDA、不用配环境、不用连远程…

作者头像 李华
网站建设 2026/10/18 20:08:49

用AI编程助手Codex七天从零开发中国象棋App并上架App Store全记录

1. 从零到上架:一个假期项目的完整拆解国庆七天假期,说长不长,说短也不短。出门看人海还是在家躺平,每年都是同样的选择题。今年我选了个第三条路——用 AI 编程助手 Codex,从零开始搓一款《中国象棋》App,…

作者头像 李华