Flutter 把第三方 UI 库渐进迁回 Material 3:组件映射总表 + 四批次替换实战

Flutter 把第三方 UI 库渐进迁回 Material 3:组件映射总表 + 四批次替换实战

作者:FungLeo | 适用:Flutter 3.x / Material 3

场景:早期引入的第三方 UI 库不太稳,想换回原生 M3,又不敢一刀切。

本文要点

  • 第三方 UI 库不是不能引,但 0.x 版本 + 样式钩子少,长期项目迟早撞墙
  • 迁移铁律:先有替代品,再替换,绝不裸删(裸删 = 把设计决策塞进体力活,必乱)
  • 动手前 grep 摸底 + 产出组件映射总表,把决策前置
  • 四批次按风险排序,高频组件反而靠后,每批可独立回滚
  • 移除依赖是最后一步:grep 扫全量源码确认零残留,完整重建验证

前言

前文 B01 从 0 搭品牌主题四件套 把主题层搭好了,这篇聊聊怎么把第三方 UI 库的组件一点点换上去。

我们项目早期引了一套第三方 UI 库(TDesign Flutter,0.2.x 版本)。当时想法很朴素:现成组件拿来就用,省时间,何必造轮子。

一开始确实爽。但用了一阵,问题陆续冒出来了:

  • 某些组件在 Android 上白屏,换机型又好了,难稳定复现;
  • 小版本之间 API 会变,升一次得改一圈调用;
  • 视觉细节跟设计稿对不齐,样式没暴露出来,改不动;
  • 最要命的,出问题只能等上游修,自己插不上手。

那段时间挺纠结,一开始想"再忍忍"。后来算了笔账------每月因这套库损耗的排查时间,其实已经超过一次性迁移成本了。于是决定:整体迁回 Material 3 原生 + 自研公共组件

但"换 UI 底层"最忌一次性全改。我见过有团队开个大分支 all in,改三周,合主干时冲突炸裂,测试还没法测------整个 App 都变了,根本不知道该重点回归哪儿。所以我用渐进式四批次替换。这篇把整个过程完整记一下。

第一步:先 grep 摸底,把家底摸清楚

动手前,先搞清楚要改多少东西。别凭感觉,凭感觉估的工作量一般能差三倍。

我的做法是直接 grep 全项目,统计每个三方组件出现次数:

sh 复制代码
# 统计每个组件出现次数,从多到少排序
grep -rho "TD[A-Za-z]*" lib/ | sort | uniq -c | sort -rn

# 看某个组件具体分布在哪些文件,加 -l
grep -rl "TDButton" lib/

跑出来大概是这么个分布:

sh 复制代码
🔴 TDButton(~15 处)、TDToast(~20 处)  # 高频,影响面最广
🟡 TDTextarea(3)、TDPicker(2)、TDStepper(2)、TDNavBar(1)  # 中频
🟢 TDIcons(~20 个不同图标名)           # 纯图标,最好换

这张表出来,心里就有底了:真正麻烦的不是数量,而是高频组件牵一发动全身TDToast 二十处散在各种异步回调里,改错一个可能就是线上一个静默失败。据此排了替换优先级,并产出一张组件映射总表。

第二步:产出组件映射总表

这一步我强烈建议做,且写成文档存下来,别只放脑子里。因为迁移不是一天干完的,中间会被别的需求打断,三天后回来早忘了 TDPicker 换成啥。多人协作没这张表,两人会替换出两种写法,那还不如不迁。

表大概长这样(节选):

旧组件 M3 / 自研替代 备注
TDIcons.xxx Icons.xxx 一一对应,最简单
TDButton(主按钮) FilledButton 圆角走 B01 的主题覆写
TDButton(次按钮) OutlinedButton / TextButton 按视觉层级选
TDToast 自研 AppToast 统一入口,后续 B04 会展开讲公共组件库
TDAlertDialog 自研 AppDialog.confirm 收敛掉 builder 样板
TDNavBar AppBar 主题里已统一样式
TDTextarea 自研 AppTextarea 带字数计数浮层
TDPicker showModalBottomSheet + 自绘 M3 无直接对应
TDStepper 自绘 M3 无直接对应

映射表里有几个坑,提前说:

一是"一对多"。TDButton,第三方库常用一个组件加 type 参数搞定所有按钮形态;M3 拆成 FilledButton / OutlinedButton / TextButton 三个独立组件。所以你不能无脑替换,得一处一处看它原来是什么形态。这是整个迁移里最费眼力的部分。

二是"没有对应物"。 步进器、日期选择器这些,M3 没提供或形态不合适,只能自绘。这部分工作量最大,单独排批次,别混在常规替换里。

三是行为差异。 这个最阴险。比如三方 Toast 可能"自动排队、后一个等前一个消失",而你用 ScaffoldMessenger 自己实现的是"后一个直接顶掉前一个"。视觉上都是弹提示,但批量操作场景下表现完全不同。这种差异 flutter analyze 查不出,只能靠真机点。

第三步:先搭"替代品",再动手替换

这条我最想强调:不要边删边想新写法。

我一开始就是这么干的------删掉一个 TDToast,现场想"那我用什么呢",随手写了个 ScaffoldMessenger.of(context).showSnackBar(...)。改到第五处,发现前面四处样式、时长、位置全不一样,因为每次都是现场发挥。

正确顺序是:

  1. 先按 B01 把主题四件套搭好,让原生组件样式先对上设计稿;
  2. 再把公共组件抽出来(AppDialog / AppBottomSheet / AppSearchBar / AppToast,后续 B04 细讲);
  3. 放一个 preview 分支,专门做个测试页,把所有形态摆出来验一遍;
  4. 确认组件质量过关,再回主干逐页替换。

这样替换时你就是个"体力活工人",机械地把旧的换成新的,不需要动脑做设计决策。脑子越不需要动,出错概率越低,这活儿也才安心交给别人一起干。

第四步:四批次渐进替换

批次按风险排,不是按工作量:

批次 内容 风险
1 主题搭建 + 组件测试页
2 低风险:图标 Icons.xxx / AppToast / AppDialog
3 中风险:搜索栏 / 表单区块 / 底部操作栏
4 自绘:步进器、日期选择器等无直接 M3 对应的

这里有个反直觉的点:高频组件(Button / Toast)影响面最大,为什么反放在靠后批次?

因为影响面大,意味着一旦替代品有问题,波及页面也最多。你得先让替代品在小范围跑一段时间,确认没毛病,再拿它去铺二十个页面。反过来,先铺二十个页面再发现问题,你就得改二十遍。

每一批替换完,跑一遍静态检查再上真机:

sh 复制代码
# 静态检查,先把编译期和 lint 问题清掉
flutter analyze

# 真机跑一遍,重点看这一批涉及的页面
flutter run

关键是:单批可独立回滚。 每批单独提交、单独合并,哪批出问题 git revert 掉那个提交就行,不影响其他批次。渐进式最大价值不是改得快,是出事时退得起

第五步:彻底移除依赖

全量替换完成、确认无残留之后,才从 pubspec.yaml 删依赖:

yaml 复制代码
dependencies:
  flutter:
    sdk: flutter
  # 移除这行(迁移完成后)
  # tdesign_flutter: ^0.2.7
sh 复制代码
flutter pub get
flutter clean && flutter build apk   # 完整重建一次,确认无编译残留

删之前,一定要把"零残留"确认干净。我有教训:

确认"零残留"要用 grep整个源码 ,而不只看"还有没有 import"。有些地方 import 删了,但注释里、字符串里、甚至某个 // TODO: 改成 TDPicker 里还留着旧名字。这些不影响编译,但会误导后面接手的人。

sh 复制代码
# 扫源码,包括注释和字符串
grep -rn "tdesign\|TDesign\|TD[A-Z]" lib/

# 别忘了 pubspec.lock 和各平台目录也扫一眼
grep -rn "tdesign" pubspec.lock ios/ android/

还有个容易忘的:删依赖后一定要完整重建,而不是热重载。 热重载不会重新解析依赖,改完看着正常,等 CI 一跑才发现某个文件还在引用已删的包。我就这么被 CI 打过一次脸。

那我当初到底该不该引这个库?

各位看官可能问:是不是一开始就不该用第三方 UI 库?

不能这么说。事后诸葛亮谁都会,但当时决策在信息条件下合理------项目要快速出原型,现成组件确实省时间。

我现在判断标准大概这样:

  • 短周期 / 原型验证:用三方库,快就是一切,反正不长期维护。
  • 长期维护项目:如果库版本号还在 0.x,社区活跃度一般,要谨慎。0.x 意味着作者保留随时改 API 的权利,这是人家明说的。
  • 设计规范强的项目:三方库样式定制能力是关键。没暴露足够样式钩子,迟早撞墙。

至于"要不要自己造轮子",我的看法:别造完整 UI 库,但可以造一层薄薄的封装。 像后面 B04 那种基于原生 M3 的公共组件,代码量不大、完全可控,出问题自己就能改。这个投入产出比,我觉得划算。

小结

好啦,这套迁移复盘到这。核心几条:

  1. 迁移 = 先有替代品,再替换,别裸删。删了再想写法,等于把设计决策塞进体力活,必乱。
  2. 动手前先 grep 摸底 + 产出组件映射总表,把决策前置,替换阶段才能机械化推进。
  3. 渐进四批次,按风险排序,每批可验证、可回滚,避免"大爆炸式重构"。
  4. 高频组件放靠后批次。影响面越大,越要等替代品稳了再动。
  5. 移除依赖是最后一步,grep 扫全量源码确认零残留,完整重建验证。
  6. 三方库不是不能用,但长期项目要看版本号和定制能力,0.x 版本心里有数。

回头看,这次迁移最大收获不是"用上了 M3",而是把 UI 控制权拿回了自己手里。以前样式问题只能提 issue 等着,现在打开自己的组件文件改两行就完事。这种踏实感,值。

如果这篇对您有点用,希望看官您用发财的小手点个小赞哈!要是您也做过类似的 UI 库迁移,欢迎在评论区分享一下您的批次划分思路,小可在这边谢谢各位看官了哈!


相关阅读

本文由 FungLeo 主导,Deepseek 优化校阅,转发请注明首发地址,谢谢大家!

相关推荐
头茬韭菜4 小时前
5.5 权限交互:渐进式信任的 UI 设计
ui·交互
GitLqr1 天前
iOS 27 强制要求 UISceneDelegate:UIKit 和 Flutter 开发者该如何应对?
flutter·ios·全栈
imperialeast1 天前
WinAXP音乐播放器8月4日更新
windows·算法·ui
蜡台1 天前
Flutter HTTP 请求完整详解
网络协议·flutter·http·dart
9765033351 天前
iOS 上架/审核 4.3a Cocos 2026最新方案解读
flutter·ios·swift·cocos2d·ios开发
FungLeo2 天前
Flutter 接入 Alice 调试浮窗:一个顶层 final 抢跑,把 release 网络整没了
网络·flutter
恋猫de小郭2 天前
Flutter hit,一个可以灵活控制溢出点击的第三方包
android·前端·flutter
天天进步20153 天前
UI-TARS 源码解析 #14:parse_action_to_structure_output:从 Thought/Action 文本到动作字典
ui
_ZHOURUI_H_3 天前
Unity MyFramework 用法说明(二十五):使用 AtlasManager 统一管理图集与 Sprite 引用
ui·unity·游戏引擎·unity3d·游戏开发