Flutter 项目升级SDK

7 阅读8分钟

Flutter 老项目升级:从 Dart 2 / 旧 Gradle 到 Flutter SDK 3.38

前言

把几年前的 Flutter 工程拉到 Flutter 3.38.x(Dart 3.10+) 上,往往不是「改一行 SDK 版本」就结束:依赖要满足 Dart 3Android 要跟上 AGP 8 + 新 Flutter Gradle 插件iOS 则要关注 部署版本、CocoaPods、Xcode 与隐私清单。老插件在 Android 侧常卡在 namespace,在 iOS 侧则可能是 Pod 最低系统版本 / Swift 与工程设置不一致。下面按实践整理检查清单与建议。


一、先定「目标工具链」,再动 pubspec

1. environment.sdk 是硬门槛

老项目常见写法:


environment:

sdk: '>=2.19.6 <3.0.0'

Dart 3 下会直接无法解析依赖。应改为与当前 Flutter 自带的 Dart 对齐,例如:


environment:

sdk: ^3.10.0

要点:以本机 flutter --version 里的 Dart 版本为准;^3.10.0 表示「至少 3.10,小于 4.0」,与 Flutter stable 通常一致。

2. 依赖升级:「能 resolve」≠「能编译」

flutter pub outdatedResolvable 一列是重要参考,但还要注意:

情况建议
主版本升级(如 flutter_bloc 8→9)看 changelog,重点看 破坏性变更
分析器报 depend_on_referenced_packages对「直接 import 的包」在 pubspec显式声明(例如单独依赖 bloc
某包只在代码里 import 但未声明补上依赖,避免后续 CI / 严格分析失败

3. flutter_lints 放哪里

flutter_lints 应放在 dev_dependencies,不要和运行时依赖混在一起;升级时可顺带升到与模板接近的版本(如 ^6.0.0),再逐步消化新 lint。


二、Android:从「apply flutter.gradle」到「Flutter Gradle Plugin」

1. 为什么要改

较新的 Flutter 模板使用:

  • settings.gradle 里的 pluginManagement + dev.flutter.flutter-plugin-loader

  • app/build.gradleid "dev.flutter.flutter-gradle-plugin"

  • AGP 8.x 要求 library 模块声明 namespace,老插件常在这里翻车

旧工程典型特征:apply from: "$flutterRoot/.../flutter.gradle"、Gradle 7.x、Kotlin 1.7 等。

2. 版本对齐思路

不必自己猜数字,以 当前 Flutter SDK 里 gradle_utils.dart 的模板常量(或 flutter create 生成的新工程)为参照,例如:

典型取向(随 Flutter 版本变化,以你本机 SDK 为准)
Gradle8.12~8.14 一带
Android Gradle Plugin8.11.x 一带
Kotlin2.2.x 一带
Java17compileOptions / jvmTarget

3. app 模块必做

  • android { namespace "..." }Manifestpackage 一致或按迁移文档处理

  • compileSdk / minSdk / targetSdk 交给 flutter.compileSdkVersion 等扩展字段,减少与引擎脱节


三、iOS:Podfile、部署目标与 Xcode

iOS 没有 Android 的 AGP namespace 问题,但升级时常见坑在 CocoaPods 环境、最低系统版本、Xcode 与苹果合规。可与 Android 并行排查。

1. Podfile 里的 platform :ios


platform :ios, '13.0'

注意点说明
全局最低版本不低于 Flutter 引擎与插件 Podspec 的要求;版本过低时,某个 Pod 会在 pod install 或编译期报错
与 Xcode 一致RunnerDeployment Targetproject.pbxproj 中的 IPHONEOS_DEPLOYMENT_TARGET 应与 Podfile 思路一致;post_installflutter_additional_ios_build_settings 会帮各 target 对齐一部分编译选项,但 platform 仍是总闸门
逐步提高从 iOS 11/12 往上提到 13+ 往往更稳;本仓库当前为 13.0

升级后若编译报「某 framework 需要 iOS xx+」,优先 提高 platform :ios升级对应插件

2. CocoaPods 与终端环境

步骤说明
顺序flutter pub get,再 cd ios && pod install(或直接 flutter build ios,由工具链触发)
UTF-8部分环境下 CocoaPods 会因编码报错(如 Unicode Normalization ... ASCII-8BIT),执行前设置:export LANG=en_US.UTF-8(可写入 ~/.zshrc
清理异常时可 flutter clean,必要时删除 ios/Podsios/Podfile.lock 后重装(团队项目改 lock 需与协作约定一致)

export LANG=en_US.UTF-8

cd ios

pod install --repo-update # 网络允许时可选,更新 spec

3. Xcode 版本

  • Flutter 官方文档当前 stable 要求的 Xcode 最低版本为准;Xcode 过旧会遇到 Swift 版本、SDK API、链接器 一类错误。

  • 首次用新版本 Xcode 打开 ios/Runner.xcworkspace(注意是 workspace,不是仅 xcodeproj)。

4. Info.plist、权限文案与隐私清单

类型说明
Usage Description使用相册、相机、麦克风、定位、蓝牙等时,必须在 Info.plist 配置对应 NS*UsageDescription,否则运行期可能直接崩溃或审核被拒
ATS / 明文 HTTP若仍访问 http,需检查 App Transport Security 例外配置是否符合产品安全要求
Privacy Manifest苹果对第三方 SDK 的 隐私清单要求随时间收紧;升级插件后若 Xcode / App Store Connect 出现新的 Privacy 相关告警,需按插件文档补充 PrivacyInfo.xcprivacy 或升级插件版本

Dart 层无改动的「纯升级」也可能因 新引擎 / 新插件 触发上述检查,建议在真机或 TestFlight 走一遍核心功能。

5. 签名、Capabilities 与扩展

  • 打开 Xcode 检查 Signing & CapabilitiesBundle IdentifierTeam 是否仍有效。

  • 若使用 Push、Associated Domains、Keychain Sharing 等,升级后确认 entitlements 未丢失或与证书匹配。

6. 与 Android 的对比(心理预期)

维度AndroidiOS
典型构建错误Gradle / AGP / namespacePod 解析失败、部署版本过低、Swift / 链接
依赖管理Maven + GradleCocoaPods(由 Flutter 聚合插件 Pod)
快速无签名验证flutter build apkflutter build ios --no-codesign(适合 CI 验证能否编过)

7. iOS 侧验收建议


flutter build ios --no-codesign

通过后再在本机 Xcode Archiveflutter run 真机 验证签名与运行时权限。


四、插件与传递依赖:两类「升级杀手」

1. AGP 8:namespace 缺失

报错里若出现 「Namespace not specified」 且指向 .pub-cache/.../某插件/android/build.gradle,说明该插件 Android 部分过旧。

策略适用
换维护中的替代方案功能可用 官方或社区活跃包 替代(如状态栏用 SystemChrome + SystemUiOverlayStyle
临时 Gradle 注入 namespace仅适合你能维护的 fork,不推荐长期依赖魔法补丁
锁旧 AGP与 Flutter 3.38 推荐栈冲突,不推荐

本仓库示例status_bar_control 因 Gradle 过旧且无 namespace,已改为 SystemChrome 在 Dart 侧实现等价效果,并移除该插件。

2. Dart SDK 与传递包不兼容(例:win32

编译期若在 .pub-cache/.../win32/...类型找不到(例如与 typed_data / Uint8List 相关 API 变迁),往往是 传递依赖版本过旧

做法说明
dependency_overrides 强制新版本快速 unblock;注明注释,待上游升级后再删
flutter pub upgrade 相关链有时能自然拉高 win32,可优先试

本仓库示例:在 pubspec.yaml 中使用 dependency_overrides: win32: ^5.10.1,让解析器拿到与 Dart 3.10 匹配的 win32


五、框架与 API 迁移:建议按「弃用提示」分批改

1. WillPopScopePopScope

Flutter 3.12+ 起 WillPopScope 弃用,与 Android 预测性返回 相关。迁移要点:

  • 使用 PopScope(canPop: ..., onPopInvokedWithResult: ...)

  • 原「二次返回退出」等逻辑,在 didPop == false 分支里自行 Navigator.popexit(0)

  • iOS 上若原本不拦截返回,可保留「直接 child、不包 PopScope」的分支,避免行为回退

2. MediaQuery.textScaleFactortextScaler

系统字体缩放相关 API 已迁到 TextScaler,例如固定为不随系统缩放:


MediaQuery.of(context).copyWith(textScaler: TextScaler.linear(1.0))

3. Dio 5:DioErrorDioException

on DioError catch 与拦截器里的类型都应改为 DioException;超时判断宜用 DioExceptionType,而不是依赖 TimeoutException 的 is 判断(类型上往往对不上)。

4. logger 2.x:日志级别方法更名

分析器会提示:vt(trace)wtff(fatal)。属于机械替换,适合一次性改完。

5. 路由参数与 Dart 3 类型

as Map? + 动态下标在严格模式下容易埋雷。更稳妥的写法是:


final raw = context?.settings?.arguments;

final map = raw is Map<String, dynamic> ? raw : null;

final id = map?['entityId'] as int? ?? 0;


六、验证顺序:比「能 analyze」更重要的是「能产物」

建议顺序:

步骤目的
flutter pub get依赖可解析
dart analyze / flutter analyze消灭 error;warning / info 可分阶段
flutter build apk --debugAndroid 整条链(Gradle + 插件 + Dart 编译)
flutter build ios --no-codesigniOS 编译链(Pod + Xcode 工程,不要求当时具备签名)
flutter test至少有一个不依赖全 App 初始化的测试,避免模板测试长期失效
iOS:pod install + 真机 / ArchiveLANG=UTF-8、Xcode 打开 .xcworkspace

export LANG=en_US.UTF-8 && cd ios && pod install


七、实践注意点(速查表)

现象排查 / 建议
Gradle 报某插件 namespace升级插件或替换实现;避免长期钉死旧 AGP
Dart 编译报 pub-cache 里第三方包 类型错误dependency_overrides 或升级引入该包的直接依赖
WillPopScope 弃用告警PopScope
字体缩放相关弃用textScaler
网络层 Dio 告警DioException + DioExceptionType
测试里 pumpWidget(MyApp()) 失败main() 里有 异步初始化 / 多 Provider 时,测试要 mock简化入口
pod install 编码 / normalize 报错终端 export LANG=en_US.UTF-8(或 LC_ALL
编译提示 iOS 版本过低提高 Podfile platform,并与 Xcode Deployment Target 对齐
真机权限崩溃补全 Info.plist 各类 NS*UsageDescription

八、小结

主题核心建议
Dart / SDKenvironment.sdk 升到 Dart 3,与 Flutter 版本绑定思考
依赖主版本看 changelog;直接 import 的包要 显式依赖;lint 放 dev_dependencies
Android跟随 Flutter 模板:新 settings + Flutter Gradle Plugin + AGP 8 + Java 17
iOSplatform :iospod installUTF-8)、Xcode 版本Info.plist / 隐私清单;用 flutter build ios --no-codesign 做编译验收
老插件Android:namespace / 旧 Gradle;iOS:Pod 最低版本 / Swift;优先 换插件或升级
传递依赖win32 类 SDK 不兼容,可用 dependency_overrides 临时解决并留注释
业务代码PopScopeTextScalerDioException、logger API 为高概率改动点
验收flutter build apk +(如有 iOS)flutter build ios --no-codesign;analyze 通过不等于能发版

升级时可以先问自己:Dart 3 能否过?Android Gradle / namespace 能否过?iOS Pod 与部署版本能否过?有没有「弃用 API + 死插件」要替换? 按上表逐项划掉,大部分老项目都能稳定落到当前 stable 工具链上。