Memoria 开发记录 23:跨平台不是“编译通过”——Flutter 端侧 AI 的边界治理
前言
Flutter 能复用 UI 和大量业务逻辑,但端侧 AI 相册涉及照片权限、后台任务、原生推理库、文件访问、通知和数据库目录,这些能力在 Android、iOS、macOS 上都有不同约束。
因此,跨平台适配不能只以“项目成功编译”为标准。真正的目标是让同一项产品能力在不同平台上拥有一致语义,并在平台不支持时明确降级。
编译问题只是最表层的问题
早期提交 0b31c0b、f0249c1 和 8d070be 多次修复 macOS、iOS 与 Darwin 构建,涉及 Podfile、部署目标、entitlements、Xcode scheme 和 ObjectBox 初始化。
这些问题说明 Flutter 项目仍然依赖完整原生构建链:
- CocoaPods 决定 iOS/macOS 原生依赖;
- entitlements 决定沙盒和文件能力;
- Xcode scheme 决定构建前脚本与环境;
- Android Gradle 和 Kotlin 版本决定插件兼容;
- 原生动态库必须包含正确架构。
只在 Android 调试机运行成功,无法证明 iOS 或 macOS 能正确使用同一功能。
平台能力需要显式建模
提交 cdf082a 增加平台兼容性检查和权限配置。比起在页面中散落 Platform.isAndroid,更可靠的方法是把平台能力抽象成明确状态:
支持系统相册读取
支持受限照片授权
支持前台服务
支持后台处理
支持某推理 delegate
支持某文件导出方式
业务层根据能力决定展示和降级,而不是假设每个平台都存在相同 API。
例如 Android 有前台服务通知,iOS 的后台执行则受到更严格时间限制;macOS 有桌面文件系统,但沙盒和 entitlement 仍会限制访问。产品语义可以一致,但实现路径不能强行相同。
构建前准备应自动化
提交 67c06b8 新增 iOS profile 准备脚本并接入 Xcode scheme。构建依赖手工步骤时,开发者本机可能成功,CI 或其他成员环境却失败。
可重复构建要求:
- 自动生成或检查必要配置;
- 明确模型和大文件来源;
- 检查代码生成产物;
- 固定依赖版本;
- 对缺失环境给出可操作错误;
- 不依赖某个人本机已有的隐藏状态。
提交 35d9022 和 9131511 分别增加 Shell 与 PowerShell 启动脚本,也是在统一不同开发环境的入口。
原生代码应保持最小边界
端侧 AI 很容易不断向 MainActivity、AppDelegate 和 C++ Bridge 中加入逻辑。短期看能快速打通能力,长期却会让 Flutter 与原生状态机重复。
当前更合理的边界是:
- 原生层只实现 Flutter 插件无法提供的系统动作;
- 媒体业务和任务编排尽量留在 Dart;
- 推理运行时通过稳定插件或明确 FFI 接口接入;
- 平台不支持时返回结构化 capability,而不是抛出模糊异常。
提交 3467cb5 将 Android 构建迁移到内置 Kotlin,也是减少额外工具链差异的一部分。
跨平台验证应该验证什么
编译成功只覆盖依赖和语法。端侧相册至少还需要验证:
- 权限首次申请、拒绝、limited 和重新选择;
- 读取缩略图、原图、视频和动态照片;
- 后台任务暂停、恢复与系统终止;
- 数据库初始化和升级;
- 模型加载、实际后端和向量一致性;
- 导出、分享和文件可见性;
- 应用重启后的状态恢复。
同一功能在平台上无法满足完整语义时,应明确标记实验或不可用,不应用空实现伪装成功。
总结
Flutter 提供了跨平台代码复用,但不会自动消除平台差异。真正稳定的跨平台工程,需要把系统差异变成显式能力,并建立可重复构建和验证流程。
关键经验包括:
- 编译通过只是跨平台适配的第一步;
- 平台能力应显式建模,不要散落条件判断;
- 构建前准备和启动入口必须自动化;
- 原生层保持最小系统边界;
- 不支持的能力要明确降级;
- 验证应覆盖权限、后台、媒体、模型和恢复,而不只是构建。
对应提交:0b31c0b、cdf082a、35d9022、9131511、f0249c1、3653cc3、67c06b8、8d070be、3467cb5。
浙公网安备 33010602011771号