Repository: flutter
Issue: Gradle buildscripts of Flutter plugins interfere with `app` project
Labels: platform-android,tool,t: gradle,P2,team-android,triaged-android

I'm trying to modernize the Gradle buildscripts of the Android part of my Flutter app, but I'm encountering problems.

### Use case

I want to get rid of the `allprojects` in my `android/build.gradle`, because it's an anti-pattern.

I don't want my `app` project to add repositories to all the other subprojects (i.e. Flutter plugins). And vice versa: I also don't want buildscripts of other Flutter plugins to add repositories to my `app` project (or generally, modify it in any way.)

A way to enforce this is to set [`RepositoriesMode.FAIL_ON_PROJECT_REPOS`](https://docs.gradle.org/8.2.1/javadoc/org/gradle/api/initialization/resolve/RepositoriesMode.html) in the `dependencyResolutionManagement` block (see `android/settings.gradle` below). This is also known as [Central repositories declaration](https://docs.gradle.org/8.2.1/javadoc/org/gradle/api/initialization/resolve/RepositoriesMode.html). There are also other [`RepositoriesMode`](https://docs.gradle.org/8.2.1/javadoc/org/gradle/api/initialization/resolve/RepositoriesMode.html)s available.

Another (very experimental) way to block plugin projects from modifying the `app` project is [Project Isolation](https://gradle.github.io/configuration-cache/#project_isolation).

> In the experiment, the configuration model of Gradle projects are "isolated" from each other. This means that build logic, such as build scripts or plugins, applied to a project cannot directly access the configuration of another project.

### Example (part 1)

I created a new blank Flutter app with `flutter create example --platforms android --empty`.

I proceed delete the following lines from `android/build.gradle`:

```diff
-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
}
```

At this point, I expect my Android build to fail, becuase it won't be able to
resolve its dependencies. And so it does:

<details>
<summary>Failing build</summary>

```
$ flutter build apk --debug


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:checkDebugAarMetadata'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
   > Could not find org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.7.10.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.7.10/kotlin-stdlib-jdk7-1.7.10.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app
   > Could not find androidx.lifecycle:lifecycle-common:2.2.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/lifecycle/lifecycle-common/2.2.0/lifecycle-common-2.2.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.lifecycle:lifecycle-common-java8:2.2.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/lifecycle/lifecycle-common-java8/2.2.0/lifecycle-common-java8-2.2.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.lifecycle:lifecycle-runtime:2.2.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/lifecycle/lifecycle-runtime/2.2.0/lifecycle-runtime-2.2.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.fragment:fragment:1.1.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/fragment/fragment/1.1.0/fragment-1.1.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.annotation:annotation:1.1.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/annotation/annotation/1.1.0/annotation-1.1.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.tracing:tracing:1.0.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/tracing/tracing/1.0.0/tracing-1.0.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.core:core:1.6.0.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/core/core/1.6.0/core-1.6.0.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420
   > Could not find androidx.window:window-java:1.0.0-beta04.
     Searched in the following locations:
       - https://storage.googleapis.com/download.flutter.io/androidx/window/window-java/1.0.0-beta04/window-java-1.0.0-beta04.pom
     If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
     Required by:
         project :app > io.flutter:flutter_embedding_debug:1.0.0-45f6e009110df4f34ec2cf99f63cf73b71b7a420

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 3s
Running Gradle task 'assembleDebug'...                              3.6s
Gradle task assembleDebug failed with exit code 1
```

</details>

Now I'm adding a Flutter plugin – let's say a popular and widely-used [`package:location`](https://pub.dev/packages/location):

```yaml
# pubspec.yaml

dependencies:
  location: ^5.0.0
```

and I run the build again:

```
$ flutter build apk --debug

Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Running Gradle task 'assembleDebug'...                             16.6s
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
```

We've just witnessed how a plugin project `location` modified the `repositories` of the `app` project. These lines in the plugin's buildscript were responsible for that:

https://github.com/Lyokone/flutterlocation/blob/a623e2d76c9b1d62df85e941f4718295782a4f45/packages/location/android/build.gradle#L17-L22

This is a simple example, but the larger the app gets, the more dependencies it amasses, the build becomes more and more intertwined with the buildscripts of the plugins.

### Example (part 2)

I continued the experiment – I tried to set `RepositoriesMode.FAIL_ON_PROJECT_REPOS` to find what else silently adds `repositories` to my `app` project.

**android/settings.gradle**

```gradle
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}

include ':app'
// rest of file ommitted
```

I run `flutter build apk --debug` again, and it failed because of the [Flutter Gradle Plugin](https://github.com/flutter/flutter/blob/c94e6766fd4ed376293bf026ee241ae75fd358e1/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy#L104) (aka `flutter.groovy`) itself modifying the `repositories` of my `app`:

https://github.com/flutter/flutter/blob/c94e6766fd4ed376293bf026ee241ae75fd358e1/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy#L72-L88

<details>
<summary>Failed build logs</summary>

```
$ flutter build apk --debug


FAILURE: Build failed with an exception.

* Where:
Script '/Users/bartek/fvm/versions/3.10.5/packages/flutter_tools/gradle/flutter.gradle' line: 182

* What went wrong:
A problem occurred evaluating script.
> Failed to apply plugin class 'FlutterPlugin'.
   > Build was configured to prefer settings repositories over project repositories but repository 'maven' was added by plugin class 'FlutterPlugin'

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 503ms
Running Gradle task 'assembleDebug'...                             987ms
Gradle task assembleDebug failed with exit code 1
```

</details>

### The problem

The problem is that basically, the whole ecosystem of Flutter plugins (for Android) has Gradle buildscripts that use a common anti-pattern. For more info about this, see also #124167.

### Summary

I don't see a general solution to this problem. Best we can do is educate plugin authors to fix their Gradle buildscripts to not use `allprojects`, and instead to migrate to `dependencyResolutionManagement`. A migration tool could be provided, but since Gradle buildscripts are just executable code, it might be hard to cover every edge case.

This is not a huge problem for me (except for the Gradle-perfectionist part). I'm creating this issue to hint at the situation and make _whoever might be concerned_ aware of it.

More interesting reading:

- ["Herding Elephants" by Square](https://developer.squareup.com/blog/herding-elephants)
- [How Flutter apps are compiled with Gradle for Android](https://github.com/flutter/flutter/wiki/How-Flutter-apps-are-compiled-with-Gradle-for-Android)
- #29328