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.rhkb.cn/news/17378.html

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

相关文章

C++类和对象进阶:运算符重载深度详解

C类和对象进阶&#xff1a;运算符重载 前言引入运算符重载定义语法注意事项重载为全局函数重载为成员函数运算符重载的本质 默认赋值运算符重载(默认成员函数)编译器自己生成的赋值运算符重载函数需要自己实现的场景总结默认赋值运算符重载 拷贝构造函数和赋值重载的区分验证 总…

three.js 使用geojson ,实现中国地图区域,边缘流动效果

three.js 使用geojson &#xff0c;实现中国地图区域&#xff0c;边缘流动效果 在线链接&#xff1a;https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idgeoBorder 国内站点预览&#xff1a;http://threehub.cn github地址: https://github.co…

PortSwigger——WebSockets vulnerabilities

文章目录 一、WebSockets二、Lab: Manipulating WebSocket messages to exploit vulnerabilities三、Lab: Manipulating the WebSocket handshake to exploit vulnerabilities四、Using cross-site WebSockets to exploit vulnerabilities4.1 跨站WebSocket劫持&#xff08;cro…

【数据结构初阶第十节】队列(详解+附源码)

好久不见。。。别不开心了&#xff0c;听听喜欢的歌吧 必须有为成功付出代价的决心&#xff0c;然后想办法付出这个代价。云边有个稻草人-CSDN博客 目录 一、概念和结构 二、队列的实现 Queue.h Queue.c test.c Relaxing Time&#xff01; ————————————《有没…

AVL树:高效平衡的二叉搜索树

&#x1f31f; 快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。&#x1f31f; 引言&#x1f914; 在数据结构的奇妙世界里&#xff0c;二叉搜索树&#xff08;BST&#xff09;原本是查找数据的好帮手。想象一下…

Qt Designer菜鸟使用教程(实现一个本地英文翻译软件)

1 安装Qt Designer 安装这个包的时候会自带安装 Qt Designer, 安装目录为python的安装根目录的 Lib/site-packages/qt5_applications/Qt/bin 目录下。 pip install pyqt5-tools2 新建窗体 2.1 新建主窗体 创建之后如下图&#xff1a; 设置主窗口大小&#xff1a; 设置窗…

机械学习基础-5.分类-数据建模与机械智能课程自留

data modeling and machine intelligence - CLASSIFICATION 为什么我们不将回归技术用于分类&#xff1f;贝叶斯分类器&#xff08;The Bayes Classifier&#xff09;逻辑回归&#xff08;Logistic Regression&#xff09;对逻辑回归的更多直观理解逻辑 /sigmoid 函数的导数我们…

C++ 网络编程

1. socket Socket 是一种用于网络通信的编程接口&#xff0c;它提供了一种类似于文件描述符的接口&#xff0c;允许不同计算机之间的进程进行通信。Socket 可以工作在多种协议上&#xff0c;最常用的是 TCP/IP 和 UDP/IP 协议 1.1 UDP 1.1.1 概念 UDP&#xff08;用户数据报协…

C/C++内存管理

目录 前言 1、C/C内存划分 2、C语言中的动态内存管理方式 3、C内存管理方式 3.1操作内置类型 3.2操作自定义类型 3.3为什么对应的new和delete必须搭配使用&#xff08;了解&#xff09; 4、operator new与operator delete函数 5、new和delete的实现原理 5.1内置类型 5…

微软开源GraphRAG的使用教程-使用自定义数据测试GraphRAG

微软在今年4月份的时候提出了GraphRAG的概念&#xff0c;然后在上周开源了GraphRAG,Github链接见https://github.com/microsoft/graphrag,截止当前&#xff0c;已有6900Star。 安装教程 官方推荐使用Python3.10-3.12版本&#xff0c;我使用Python3.10版本安装时&#xff0c;在…

RK3568平台开发系列讲解(调试篇)网卡队列均衡负载

🚀返回专栏总目录 文章目录 一、RPS 的介绍1. RPS 的工作原理2. RPS 配置3. 启用和调优 RPS4. RPS 优势二、下行测试iperf测试沉淀、分享、成长,让自己和他人都能有所收获!😄 RPS(Receive Packet Steering) 是一种用于提高网络接收性能的技术,通常用于多核处理器系统中…

RagFlow + Docker Desktop + Ollama + DeepSeek-R1本地部署自己的本地AI大模型工具

前期准备 首先&#xff0c;我们需要下载 Ollama 以及配置相关环境。 Ollama 的 GitHub仓库 &#xff08;https://github.com/ollama/ollama&#xff09;中提供了详细的说明&#xff0c;简单总结如下: Step1&#xff1a;下载 Ollama 下载&#xff08;https://ollama.com/dow…

变分边界详解

起因 当时看VAE论文时有这么一段&#xff0c;但是看完直接一头雾水&#xff0c;这都那跟哪&#xff0c;第一个公式咋做的变换就变出那么一堆。网上搜了很多博客都语焉不详&#xff0c;只好自己来写一篇&#xff0c;希望能解答后来人的疑惑。 公式1 参考文章&#xff1a;证据…

云消息队列 ApsaraMQ Serverless 演进:高弹性低成本、更稳定更安全、智能化免运维

如今&#xff0c;消息队列已成为分布式架构中不可或缺的关键服务&#xff0c;为电商、物联网、游戏和教育等行业&#xff0c;提供了异步解耦、集成、高性能和高可靠的核心价值。 过去一年&#xff0c;我们发布了云消息队列 ApsaraMQ 全系列产品 Serverless 化&#xff0c;面向…

【蓝桥杯嵌入式】8_IIC通信-eeprom读写

全部代码网盘自取 链接&#xff1a;https://pan.baidu.com/s/1PX2NCQxnADxYBQx5CsOgPA?pwd3ii2 提取码&#xff1a;3ii2 1、电路图 这个电路允许通过I2C总线与EEPROM(M24C02-WMN6TP)和数字电位器(MCP4017T-104ELT)进行通信。EEPROM用于存储数据&#xff0c;而数字电位器可以用…

DeepSeek处理自有业务的案例:让AI给你写一份小众编辑器(EverEdit)的语法着色文件

1 DeepSeek处理自有业务的案例&#xff1a;让AI给你写一份小众编辑器(EverEdit)的语法着色文件 1.1 背景 AI能力再强&#xff0c;如果不能在企业的自有业务上产生助益&#xff0c;那基本也是一无是处。将企业的自有业务上传到线上训练&#xff0c;那是脑子进水的做法&#xff…

Java常用设计模式面试题总结(内容详细,简单易懂)

设计模式的分类 创建型模式&#xff1a;通过隐藏对象创建的细节&#xff0c;避免直接使用 new 关键字实例化对象&#xff0c;从而使程序在判断和创建对象时更具灵活性。常见的模式包括&#xff1a; 工厂模式抽象工厂模式单例模式建造者模式原型模式 结构型模式&#xff1a;通…

使用HX搭建UNI-APP云开发项目(适合新手小白与想学云开发的宝子)

什么是uni-app云开发 uni-app云开发是uni-app提供的一套后端服务,它可以帮助开发者快速搭建起一个完整的后端服务,包括数据库、云函数、存储等。开发者只需要关注前端页面的开发,后端服务由uni-app云开发提供。 uni-app云开发的优势: 快速搭建后端服务:uni-app云开发提供了…

零基础学CocosCreator·第九季-网络游戏同步策略与ESC架构

课程里的版本好像是1.9&#xff0c;目前使用版本为3.8.3 开始~ 目录 状态同步帧同步帧同步客户端帧同步服务端ECS框架概念ECS的解释ECS的特点EntityComponentSystemWorld ECS实现逻辑帧&渲染帧 ECS框架使用帧同步&ECS 状态同步 一般游戏的同步策略有两种&#xff1a;…

最新版Edge浏览器集成ActiveX控件之金山WpsDocFrame控件

背景 WpsDocFrame控件‌是由金山公司开发的ActiveX控件&#xff0c;主要用于OA系统中&#xff0c;支持在浏览器中嵌入WPS文档的查看和编辑功能。 allWebPlugin中间件是一款为用户提供安全、可靠、便捷的浏览器插件服务的中间件产品&#xff0c;致力于将浏览器插件重新应用到所有…