Flutter Gradle 命令式插件正式移除,你迁移旧版 Gradle 配置了吗?

在 Flutter 3.29 版本里官方正式移除了 Flutter Gradle Apply 插件,其实该插件自 3.19 起已被弃用,同时 Flutter 团队后续也打算把 Flutter Gradle 从 Groovy 转换为 Kotlin,并将其迁移到使用 AGP(Android Gradle Plugin)的公共 API,所以这个改动有望降低在发布新 AGP 版本时损坏的频率,并减少基于构建的回归。

从这里也可以看出来,Flutter 团队也为 AGP 升级迭代适配感到“头痛”。

所以如果你的项目是在 3.16 版本之前创建,但一直尚未迁移,那么在 3.29 版本下肯定会受到直接影响,比如之前 Flutter 工具在构建项目时有警告:“You are applying Flutter's main Gradle plugin imperatively”,那么基本可以确定会 3.29 版本会无法正常运行,开发者需要手动进行迁移。

各种版本对应关系

首先要说一些额外前置关系,和本文没直接关联,适合在迁移时还有升级需求的,如果不感兴趣可以直接看第二部分,因为在 Android Gradle 里,AGP 相关升级可以说是 Android 开发者最头疼的问题之一,这里面除了涉及 JDK 、Gradle、AGP、Kotlin、KGP(Kotlin Gradle Plugin)等版本之外,甚至还和 Android Studio 的版本有关系,而 Android Studio 正式版又刚刚度过 10 周年, 种种因素之下,想要不那么难受的升级迁移,或者你需要简单理清下他们的版本对应关系

比如之前就出现过,由于某些官方的 androidx 开始升级到了 JDK 21 ,但是官方在旧版 AGP 中没有正确处理,从而引发如 D8 Cannot invoke "String.length()" because "<parameter1> 等相关 issue 。

首先我们以 JDK 作为视角,简单看看 Android 构建中的 JDK 关系,大概可以知道 JDK 在 Gradle、Kotlin、Android Studio 和 AGP 里的角色:

然后,我们再简单看看,在不同 Android Studio 里,默认自带的 JDK 版本是什么:

  • Android Studio Ladybug ( JDK 21
  • Android Studio Koala
  • Android Studio Jellyfish
  • Android Studio Iguana
  • Android Studio Hedgehog
  • Android Studio Giraffe
  • Android Studio Flamingo ( JDK 17
  • Android Studio Electric Eel
  • Android Studio Dolphin
  • Android Studio Chipmunk
  • Android Studio Bumblebee
  • Android Studio Arctic Fox (JDK 11

接着,我们再看看 Android Studio 和 AGP 版本之间的对应关系:

然后我们再看 Java version 和 Gradle 之间的版本对应关系:

最后是 AGP 和 Gradle 版本之间的关系:

到这里,我们可以直观知道,Gradle 版本其实和 Java 版本有关系的,而不同 Android Studio 默认自带的 JDK 版本是不同的,所以在迁移过程中,你需要确定:

  • Android Studio 版本
  • AGP 版本
  • Gradle 版本
  • JDK 版本

只有这四者之间版本范围合适,你才可以减少在迁移升级版本的过程中冲突踩坑,当然 Android Studio 内置的 JDK 版本是支持手动切换的 ,你可以在设置里手动下载想要的 JDK 版本:

当然,如果你不用 Andriod Studio ,只用 VSCode 的话,那么就可以减少考虑 Android Studio 版本和内置 JDK 的问题。

接着,其实还有 KGP 、 Kotlin 和 AGP 的版本对应关系问题,因为在 Flutter 里,各种 Plugin 和主工程都可能有不同的 kotlin versoin:

关于 KGP 、Gradle 和 AGP 的对应关系:

可以看到,在选择对应 KGP 的时候,最好是在合适 AGP 范围内,不然编译可能也会出现意料之外的报错。

迁移

从 Flutter 3.16 开始,官方就增加了使用 Gradle 的声明式插件 {} 块(也称为插件 DSL)应用插件的支持,而 DSL 会要求静态定义插件,这也是 plugins {} 块机制和传统 apply() 的差异之一,例如:

  • plugins{} 只能在项目的构建脚本 build.gradle(.kts)settings.gradle(.kts) 文件中使用,并且它必须出现在任何其他块之前,同时不能在 script plugins 或 init 脚本中使用
  • plugins {} 块不支持任意代码,它必须是无副作用,每次都产生相同的结果
  • plugins{} 必须是构建脚本中的顶级语句,它不能嵌套在另一个结构中(如 if 语句或 for 循环)

所以,迁移时,我们首先需要找到项目当前使用的 Android Gradle Plugin (AGP) 和 Kotlin 的值,一般都在 /android/build.gradle 文件的 buildscript 里,比如这里的 kotlin_versioncom.android.tools.build:gradle

buildscript {ext.kotlin_version = '1.7.10'repositories {google()mavenCentral()}dependencies {classpath 'com.android.tools.build:gradle:7.3.0'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"}
}allprojects {repositories {google()mavenCentral()}
}rootProject.buildDir = '../build'
subprojects {project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {project.evaluationDependsOn(':app')
}tasks.register("clean", Delete) {delete rootProject.buildDir
}

接下来,需要将项目下 /android/settings.gradle 的内容替换为以下内容,这里的 {agpVersion}{kotlinVersion} 就是前面原本的数值 :

pluginManagement {def flutterSdkPath = {def properties = new Properties()file("local.properties").withInputStream { properties.load(it) }def flutterSdkPath = properties.getProperty("flutter.sdk")assert flutterSdkPath != null, "flutter.sdk not set in local.properties"return flutterSdkPath}()includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")repositories {google()mavenCentral()gradlePluginPortal()}
}plugins {id "dev.flutter.flutter-plugin-loader" version "1.0.0"id "com.android.application" version "{agpVersion}" apply falseid "org.jetbrains.kotlin.android" version "{kotlinVersion}" apply false
}include ":app"

如果你还有一些其他参数配置,需要确保将它们放在 pluginManagement {}plugins {} 块之后,正如前面所说, Gradle 强制要求不能将其他代码放在这些块之前。

接着,从 /android/build.gradle 中删除整个 buildscript 块:

默认情况下,android/build.gradle 文件应该只剩下这个样子:

allprojects {repositories {google()mavenCentral()}
}rootProject.buildDir = '../build'
subprojects {project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {project.evaluationDependsOn(':app')
}tasks.register("clean", Delete) {delete rootProject.buildDir
}

接着你还需要对代码 android/app/build.gradle 进行一些调整,例如删除以下 2 个使用旧版命令式 apply 方法的代码块:

然后再次添加对应的插件,但这次使用 Plugin DSL 语法,同样需要在文件的最顶部:

plugins {id "com.android.application"id "kotlin-android"id "dev.flutter.flutter-gradle-plugin"
}

最后,如果您的 dependencies 块包含对 "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 的依赖项,还需要删除该依赖项:

image-20250212224117799

以上是属于官方默认最简配置下的迁移,如果你还是用了其他 classpathapply 模块,那么你还需要将他们都移除:

然后将它们添加到应用 android/settings.gradle 文件的 plugins 块里面:

plugins {id "dev.flutter.flutter-plugin-loader" version "1.0.0"id "com.android.application" version "{agpVersion}" apply falseid "org.jetbrains.kotlin.android" version "{kotlinVersion}" apply false/// 这个id "com.google.gms.google-services" version "4.4.0" apply false/// 这个id "com.google.firebase.crashlytics" version "2.9.9" apply false
}

并且在 android/app/build.gradle 同步添加:

plugins {id "com.android.application"id "dev.flutter.flutter-gradle-plugin"id "org.jetbrains.kotlin.android"/// 这个id "com.google.gms.google-services"/// 这个id "com.google.firebase.crashlytics"
}

最后,以下是一个简单迁移后的 git diff patch 参考:

Index: android/app/build.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/android/app/build.gradle b/android/app/build.gradle
--- a/android/app/build.gradle	(revision 69dfe7ed0d762bfd35e470fc31d2aebf1e1690bf)
+++ b/android/app/build.gradle	(revision 1adf2a436b02e7af99121553eb67d7880ad91571)
@@ -1,3 +1,9 @@
+plugins {
+    id "com.android.application"
+    id "kotlin-android"
+    id "dev.flutter.flutter-gradle-plugin"
+}
+def localProperties = new Properties()def localPropertiesFile = rootProject.file('local.properties')if (localPropertiesFile.exists()) {
@@ -6,14 +12,6 @@}}-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
-    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"apply from: "exported.gradle"android {
@@ -31,9 +29,9 @@// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).applicationId "com.shuyu.gsygithub.gsygithubappflutter"minSdkVersion 21
-        targetSdkVersion 31
+        targetSdkVersion 33versionCode 54
-        versionName "4.0.1"
+        versionName "5.0.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}@@ -70,9 +68,4 @@source '../..'}-dependencies {
-    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
-    testImplementation 'junit:junit:4.12'
-    androidTestImplementation 'androidx.test:runner:1.1.1'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
-}
+dependencies {}
Index: android/build.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/android/build.gradle b/android/build.gradle
--- a/android/build.gradle	(revision 69dfe7ed0d762bfd35e470fc31d2aebf1e1690bf)
+++ b/android/build.gradle	(revision 1adf2a436b02e7af99121553eb67d7880ad91571)
@@ -1,16 +1,3 @@
-buildscript {
-    ext.kotlin_version = '1.8.10'
-    repositories {
-        google()
-        jcenter()
-    }
-
-    dependencies {
-        classpath "com.android.tools.build:gradle:7.0.3"
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-    }
-}
-allprojects {repositories {google()
Index: android/settings.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/android/settings.gradle b/android/settings.gradle
--- a/android/settings.gradle	(revision 69dfe7ed0d762bfd35e470fc31d2aebf1e1690bf)
+++ b/android/settings.gradle	(revision 1adf2a436b02e7af99121553eb67d7880ad91571)
@@ -1,15 +1,25 @@
-include ':app'
+pluginManagement {
+    def flutterSdkPath = {
+        def properties = new Properties()
+        file("local.properties").withInputStream { properties.load(it) }
+        def flutterSdkPath = properties.getProperty("flutter.sdk")
+        assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+        return flutterSdkPath
+    }()-def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")-def plugins = new Properties()
-def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
-if (pluginsFile.exists()) {
-    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+    repositories {
+        google()
+        mavenCentral()
+        gradlePluginPortal()
+    }}-plugins.each { name, path ->
-    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
-    include ":$name"
-    project(":$name").projectDir = pluginDirectory
+plugins {
+    id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+    id "com.android.application" version "7.0.3" apply false
+    id "org.jetbrains.kotlin.android" version "1.8.10" apply false}
+
+include ":app"
\ No newline at end of file

参考链接

  • https://docs.flutter.dev/release/breaking-changes/flutter-gradle-plugin-apply

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/69846.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

15.Python网络编程:进程池、进程间通信、多线程、进程和线程区别、网络通信、端口、IP地址、socket、UDP、TCP、http

1. 进程池&#xff08;Process Pool&#xff09; 进程池是通过将多个进程放入池中管理来避免频繁地创建和销毁进程&#xff0c;提高效率。Python 提供了 multiprocessing.Pool 类来实现进程池&#xff0c;它可以用于并行计算任务。 示例&#xff1a;使用进程池 from multipr…

Vue 中报错 TypeError: crypto$2.getRandomValues is not a function

问题 在新建的项目中&#xff0c;使用的是 npm init vue 创建项目后&#xff0c;执行命令 npm i &#xff0c;然后去 npm run dev 这个时候报错 TypeError: crypto$2.getRandomValues is not a function 起初是以为搞错了&#xff0c;然后再删掉 node_modules 和 package-lo…

如何通过挂载debugfs来访问内核调试信息

1. DebugFS 的作用 bugFS 的作用 内核调试接口&#xff1a;允许内核模块或子系统在 DebugFS 中创建虚拟文件或目录&#xff0c;暴露调试信息。 动态交互&#xff1a;用户可以直接读写这些文件来查看或修改内核状态&#xff08;如调整日志级别、触发特定操作&#xff09;。 零…

001 SpringCloudAlibaba整合 - Nacos注册配置中心、Sentinel流控、Zipkin链路追踪、Admin监控

SpringCloudAlibaba 文章目录 SpringCloudAlibaba1.版本依赖关系2022.x 分支2021.x 分支2.2.x 分支 组件版本关系 2.基础项目构建1.引入全局pom文件2.创建对应的模块 3.SpringBootAdmin监控服务整合1.cloud-admin服务搭建1.导入服务端依赖2.主启动类添加EnableAdminServer注解启…

常用的网络安全设备

一、 WAF 应用防火墙 范围&#xff1a;应用层防护软件 作用&#xff1a; 通过特征提取和分块检索技术进行模式匹配来达到过滤&#xff0c;分析&#xff0c;校验网络请求包的目的&#xff0c;在保证正常网络应用功能的同时&#xff0c;隔绝或者阻断无效或者非法的攻击请求 可…

Jenkins 新建配置Pipeline任务 三

Jenkins 新建配置Pipeline任务 三 一. 登录 Jenkins 网页输入 http://localhost:8080 输入账号、密码登录 一个没有创建任务的空 Jenkins 二. 创建 任务 图 NewItem 界面左上角 New Item 图NewItemSelect 1.Enter an item name&#xff1a;输入任务名 2.Select an ite…

如何构建有效的人工智能代理

目录 什么是 AI 代理? 何时应使用 AI 代理? 人工智能代理的构建模块 构建 AI 代理的常用方法 1. 提示链接(分步说明) 2.路由(将任务发送到正确的地方) 3.并行处理(同时做多件事) 4. 协调者和工作者 AI(团队合作) 5. 评估器和优化器(修复错误) 如何让人工…

linux 下连接mysql(下)

case 表达式 表t1中的数据如下。 select * from t1; ---------------------------- | id | student_no | name | age | ---------------------------- | 3 | 202501 | ll | 10 | | 4 | 202502 | tt | 15 | ----------------------------如果学号是202501,…

hivemetastore 连接过多导致sql查询慢

MetaStore (DB)修改对应的处理连接数maxConnectionsPerPartition建议设置成 100 (不能超过最大值300)&#xff0c;重启Hive服务。 建议值100是根据与工行规模相当集群的设置作参考&#xff0c;尽量大满足连接数请求的同时考虑connection占用资源(如果connection太多会占用太多的…

sqli-labs布尔盲注

通过?id1及?id1判断是否存在sql注入的漏洞 ?id1有回显 ?id1无回显 再使用?id1 and 11 --及?id1 and 12 --判断是否为布尔盲注 ?id1 and 11 --有回显 ?id1 and 12 --无回显 为布尔盲注&#xff0c;使用python获取其中的库名&#xff0c;表名&#xff0c;列名及数据 1…

ovs源码分析

源码版本 ovs 内核部分的代码在linux内核的 /net/openswitch目录下&#xff0c;应用层控制面代码在ovs项目中。 Linux kernel: version 6.2.0 Ovs: v3.4.1 总体架构 整体结构图 ovs的架构如下图所示&#xff0c;主要由内核datapath、vswitchd、ovsdb以及用户空间的ovs-vs…

vue3实战-----使用mock模拟接口数据

vue3实战-----使用mock模拟接口数据 1.安装和配置2.创建mock数据3.使用axios测试 1.安装和配置 安装依赖:https://www.npmjs.com/package/vite-plugin-mock pnpm install -D vite-plugin-mock mockjs在 vite.config.js 配置文件启用插件: import { viteMockServe } from vit…

DeepSeek+Excel 效率翻倍

2025年初&#xff0c;DeepSeek以惊人的效率突破技术壁垒&#xff0c;用极低的成本实现了与行业顶尖AI相媲美的性能&#xff0c;瞬间成为全球科技领域的热门话题。 那么AI工具的普及将如何改变我们的工作方式&#xff1f;Excel会被取代吗&#xff1f; 今天&#xff0c;珠珠带你…

Rhel Centos环境开关机自动脚本

Rhel Centos环境开关机自动脚本 1. 业务需求2. 解决方法2.1 rc.local2.2 rc.d2.3 systemd2.4 systemd附着的方法2.5 tuned 3. 测试 1. 业务需求 一台较老的服务器上面业务比较简单,提供一个简单的网站,但已经没有业务的运维人员. 想达到的效果: 由于是非标准的apache或者nginx…

pyside6 中信号有的地方用connect有的用emit为什么

在 PySide6 的 Qt 框架中&#xff0c;connect 和 emit 是信号与槽机制的两个核心操作&#xff0c;但它们的作用完全不同&#xff1a; 1. connect()&#xff1a;建立信号与槽的绑定 作用&#xff1a;将某个信号&#xff08;Signal&#xff09;与一个槽函数&#xff08;Slot&…

React历代主要更新

一、React 16之前更新 React Fiber是16版本之后的一种更新机制&#xff0c;使用链表取代了树&#xff0c;是一种fiber数据结构&#xff0c;其有三个指针&#xff0c;分别指向了父节点、子节点、兄弟节点&#xff0c;当中断的时候会记录下当前的节点&#xff0c;然后继续更新&a…

使用 EDOT 监测由 OpenAI 提供支持的 Python、Node.js 和 Java 应用程序

作者&#xff1a;来自 Elastic Adrian Cole Elastic 很自豪地在我们的 Python、Node.js 和 Java EDOT SDK 中引入了 OpenAI 支持。它们为使用 OpenAI 兼容服务的应用程序添加日志、指标和跟踪&#xff0c;而无需任何代码更改。 介绍 去年&#xff0c;我们宣布了 OpenTelemetry…

RabbitMQ使用guest登录提示:User can only log in via localhost

guest用户默认是无法使用远程访问的&#xff0c;生产环境建议直接在对应服务器登录使用。 1、通过创建新增用户并赋予权限实现远程登录 添加新用户 rabbitmqctl add_user zjp zjp 设置管理员 rabbitmqctl set_user_tags zjp administrator 设置新用户的权限 rabbitmqctl…

Eclipse JSP/Servlet 深入解析

Eclipse JSP/Servlet 深入解析 引言 随着互联网的快速发展,Java Web开发技术逐渐成为企业级应用开发的主流。在Java Web开发中,JSP(JavaServer Pages)和Servlet是两个核心组件,它们共同构成了Java Web应用程序的基础。本文将深入解析Eclipse平台下的JSP/Servlet技术,帮…

【Uniapp】关于实现下拉刷新的三种方式

在小程序、h5等地方中&#xff0c;常常会用到下拉刷新这个功能&#xff0c;今天来讲解实现这个功能的三种方式&#xff1a;全局下拉刷新&#xff0c;组件局部下拉刷新&#xff0c;嵌套组件下拉刷新。 全局下拉刷新 这个方式简单&#xff0c;性能佳&#xff0c;最推荐&#xf…