33// found in the LICENSE file.
44
55import com.android.build.OutputFile
6- import groovy.json.JsonSlurper
76import groovy.json.JsonGenerator
87import groovy.xml.QName
98import java.nio.file.Paths
@@ -65,7 +64,7 @@ class FlutterExtension {
6564 * Specifies the relative directory to the Flutter project directory.
6665 * In an app project, this is ../.. since the app's build.gradle is under android/app.
6766 */
68- String source
67+ String source = ' ../.. '
6968
7069 /* * Allows to override the target file. Otherwise, the target is lib/main.dart. */
7170 String target
@@ -222,6 +221,9 @@ class FlutterPlugin implements Plugin<Project> {
222221 }
223222 }
224223
224+ // Load shared gradle functions
225+ project. apply from : Paths . get(flutterRoot. absolutePath, " packages" , " flutter_tools" , " gradle" , " src" , " main" , " groovy" , " native_plugin_loader.groovy" )
226+
225227 project. extensions. create(" flutter" , FlutterExtension )
226228 this . addFlutterTasks(project)
227229
@@ -357,7 +359,7 @@ class FlutterPlugin implements Plugin<Project> {
357359 // This prevents duplicated classes when using custom build types. That is, a custom build
358360 // type like profile is used, and the plugin and app projects have API dependencies on the
359361 // embedding.
360- if (! isFlutterAppProject() || getPluginList(). size() == 0 ) {
362+ if (! isFlutterAppProject() || getPluginList(project ). size() == 0 ) {
361363 addApiDependencies(project, buildType. name,
362364 " io.flutter:flutter_embedding_$flutterBuildMode :$engineVersion " )
363365 }
@@ -390,19 +392,77 @@ class FlutterPlugin implements Plugin<Project> {
390392 * Configures the Flutter plugin dependencies.
391393 *
392394 * The plugins are added to pubspec.yaml. Then, upon running `flutter pub get`,
393- * the tool generates a `.flutter-plugins` file, which contains a 1:1 map to each plugin location.
395+ * the tool generates a `.flutter-plugins-dependencies ` file, which contains a map to each plugin location.
394396 * Finally, the project's `settings.gradle` loads each plugin's android directory as a subproject.
395397 */
396- private void configurePlugins () {
397- getPluginList(). each(this . &configurePluginProject)
398- getPluginDependencies(). each(this . &configurePluginDependencies)
398+ private void configurePlugins (Project project ) {
399+ configureLegacyPluginEachProjects(project)
400+ getPluginList(project). each(this . &configurePluginProject)
401+ getPluginList(project). each(this . &configurePluginDependencies)
402+ }
403+
404+ // TODO(54566, 48918): Can remove once the issues are resolved.
405+ // This means all references to `.flutter-plugins` are then removed and
406+ // apps only depend exclusively on the `plugins` property in `.flutter-plugins-dependencies`.
407+ /**
408+ * Workaround to load non-native plugins for developers who may still use an
409+ * old `settings.gradle` which includes all the plugins from the
410+ * `.flutter-plugins` file, even if not made for Android.
411+ * The settings.gradle then:
412+ * 1) tries to add the android plugin implementation, which does not
413+ * exist at all, but is also not included successfully
414+ * (which does not throw an error and therefore isn't a problem), or
415+ * 2) includes the plugin successfully as a valid android plugin
416+ * directory exists, even if the surrounding flutter package does not
417+ * support the android platform (see e.g. apple_maps_flutter: 1.0.1).
418+ * So as it's included successfully it expects to be added as API.
419+ * This is only possible by taking all plugins into account, which
420+ * only appear on the `dependencyGraph` and in the `.flutter-plugins` file.
421+ * So in summary the plugins are currently selected from the `dependencyGraph`
422+ * and filtered then with the [doesSupportAndroidPlatform] method instead of
423+ * just using the `plugins.android` list.
424+ */
425+ private configureLegacyPluginEachProjects (Project project ) {
426+ File settingsGradle = new File (project. projectDir. parentFile, ' settings.gradle' )
427+ try {
428+ if (! settingsGradle. text. contains(" '.flutter-plugins'" )) {
429+ return
430+ }
431+ } catch (FileNotFoundException ignored) {
432+ throw new GradleException (" settings.gradle does not exist: ${ settingsGradle.absolutePath} " )
433+ }
434+ List<Map<String , Object > > deps = getPluginDependencies(project)
435+ List<String > plugins = getPluginList(project). collect { it. name as String }
436+ deps. removeIf { plugins. contains(it. name) }
437+ deps. each {
438+ Project pluginProject = project. rootProject. findProject(" :${ it.name} " )
439+ if (pluginProject == null ) {
440+ // Plugin was not included in `settings.gradle`, but is listed in `.flutter-plugins`.
441+ project. logger. error(" Plugin project :${ it.name} listed, but not found. Please fix your settings.gradle." )
442+ } else if (doesSupportAndroidPlatform(pluginProject. projectDir. parentFile. path as String )) {
443+ // Plugin has a functioning `android` folder and is included successfully, although it's not supported.
444+ // It must be configured nonetheless, to not throw an "Unresolved reference" exception.
445+ configurePluginProject(it)
446+ } else {
447+ // Plugin has no or an empty `android` folder. No action required.
448+ }
449+ }
450+ }
451+
452+ // TODO(54566): Can remove this function and its call sites once resolved.
453+ /**
454+ * Returns `true` if the given path contains an `android/build.gradle` file.
455+ */
456+ private static Boolean doesSupportAndroidPlatform (String path ) {
457+ File editableAndroidProject = new File (path, ' android' + File . separator + ' build.gradle' )
458+ return editableAndroidProject. exists()
399459 }
400460
401461 /* * Adds the plugin project dependency to the app project. */
402- private void configurePluginProject (String pluginName , String _ ) {
403- Project pluginProject = project. rootProject. findProject(" :$pluginName " )
462+ private void configurePluginProject (Map<String , Object > pluginObject ) {
463+ assert(pluginObject. name instanceof String )
464+ Project pluginProject = project. rootProject. findProject(" :${ pluginObject.name} " )
404465 if (pluginProject == null ) {
405- project. logger. error(" Plugin project :$pluginName not found. Please update settings.gradle." )
406466 return
407467 }
408468 // Add plugin dependency to the app project.
@@ -441,7 +501,7 @@ class FlutterPlugin implements Plugin<Project> {
441501 pluginProject. afterEvaluate {
442502 // Checks if there is a mismatch between the plugin compileSdkVersion and the project compileSdkVersion.
443503 if (pluginProject. android. compileSdkVersion > project. android. compileSdkVersion) {
444- project. logger. quiet(" Warning: The plugin ${ pluginName } requires Android SDK version ${ getCompileSdkFromProject(pluginProject)} or higher." )
504+ project. logger. quiet(" Warning: The plugin ${ pluginObject.name } requires Android SDK version ${ getCompileSdkFromProject(pluginProject)} or higher." )
445505 project. logger. quiet(" For more information about build configuration, see $kWebsiteDeploymentAndroidBuildConfig . " )
446506 }
447507
@@ -493,10 +553,14 @@ class FlutterPlugin implements Plugin<Project> {
493553 String ndkVersionIfUnspecified = " 21.1.6352462" /* The default for AGP 4.1.0 used in old templates. */
494554 String projectNdkVersion = project. android. ndkVersion ?: ndkVersionIfUnspecified
495555 String maxPluginNdkVersion = projectNdkVersion
496- int numProcessedPlugins = getPluginList(). size()
556+ int numProcessedPlugins = getPluginList(project ). size()
497557
498- getPluginList(). each { plugin ->
499- Project pluginProject = project. rootProject. findProject(plugin. key)
558+ getPluginList(project). each { pluginObject ->
559+ assert(pluginObject. name instanceof String )
560+ Project pluginProject = project. rootProject. findProject(" :${ pluginObject.name} " )
561+ if (pluginProject == null ) {
562+ return
563+ }
500564 pluginProject. afterEvaluate {
501565 // Default to int min if using a preview version to skip the sdk check.
502566 int pluginCompileSdkVersion = Integer . MIN_VALUE ;
@@ -530,35 +594,25 @@ class FlutterPlugin implements Plugin<Project> {
530594 return gradleProject. android. compileSdkVersion. substring(8 );
531595 }
532596
533- /**
534- * Returns `true` if the given path contains an `android/build.gradle` file.
535- */
536- private Boolean doesSupportAndroidPlatform (String path ) {
537- File editableAndroidProject = new File (path, ' android' + File . separator + ' build.gradle' )
538- return editableAndroidProject. exists()
539- }
540-
541597 /**
542598 * Add the dependencies on other plugin projects to the plugin project.
543599 * A plugin A can depend on plugin B. As a result, this dependency must be surfaced by
544600 * making the Gradle plugin project A depend on the Gradle plugin project B.
545601 */
546- private void configurePluginDependencies (Object dependencyObject ) {
547- assert(dependencyObject. name instanceof String )
548- Project pluginProject = project. rootProject. findProject(" :${ dependencyObject.name} " )
549- if (pluginProject == null ||
550- ! doesSupportAndroidPlatform(pluginProject. projectDir. parentFile. path)) {
602+ private void configurePluginDependencies (Map<String , Object > pluginObject ) {
603+ assert(pluginObject. name instanceof String )
604+ Project pluginProject = project. rootProject. findProject(" :${ pluginObject.name} " )
605+ if (pluginProject == null ) {
551606 return
552607 }
553- assert(dependencyObject . dependencies instanceof List )
554- dependencyObject . dependencies. each { pluginDependencyName ->
555- assert( pluginDependencyName instanceof String )
608+ def dependencies = pluginObject . dependencies
609+ assert( dependencies instanceof List< String > )
610+ dependencies . each { pluginDependencyName ->
556611 if (pluginDependencyName. empty) {
557612 return
558613 }
559614 Project dependencyProject = project. rootProject. findProject(" :$pluginDependencyName " )
560- if (dependencyProject == null ||
561- ! doesSupportAndroidPlatform(dependencyProject. projectDir. parentFile. path)) {
615+ if (dependencyProject == null ) {
562616 return
563617 }
564618 // Wait for the Android plugin to load and add the dependency to the plugin project.
@@ -570,52 +624,27 @@ class FlutterPlugin implements Plugin<Project> {
570624 }
571625 }
572626
573- private Properties getPluginList () {
574- File pluginsFile = new File (project. projectDir. parentFile. parentFile, ' .flutter-plugins' )
575- Properties allPlugins = readPropertiesIfExist(pluginsFile)
576- Properties androidPlugins = new Properties ()
577- allPlugins. each { name , path ->
578- if (doesSupportAndroidPlatform(path)) {
579- androidPlugins. setProperty(name, path)
580- }
581- // TODO(amirh): log an error if this plugin was specified to be an Android
582- // plugin according to the new schema, and was missing a build.gradle file.
583- // https://github.com/flutter/flutter/issues/40784
584- }
585- return androidPlugins
627+ /**
628+ * Gets the list of plugins (as map) that support the Android platform.
629+ *
630+ * The map value contains either the plugins `name` (String),
631+ * its `path` (String), or its `dependencies` (List<String>).
632+ * See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/groovy/native_plugin_loader.groovy
633+ */
634+ private List<Map<String , Object > > getPluginList (Project project ) {
635+ return project. ext. nativePluginLoader. getPlugins(getFlutterSourceDirectory())
586636 }
587637
638+ // TODO(54566, 48918): Remove in favor of [getPluginList] only, see also
639+ // https://github.com/flutter/flutter/blob/1c90ed8b64d9ed8ce2431afad8bc6e6d9acc4556/packages/flutter_tools/lib/src/flutter_plugins.dart#L212
588640 /* * Gets the plugins dependencies from `.flutter-plugins-dependencies`. */
589- private List getPluginDependencies () {
590- // Consider a `.flutter-plugins-dependencies` file with the following content:
591- // {
592- // "dependencyGraph": [
593- // {
594- // "name": "plugin-a",
595- // "dependencies": ["plugin-b","plugin-c"]
596- // },
597- // {
598- // "name": "plugin-b",
599- // "dependencies": ["plugin-c"]
600- // },
601- // {
602- // "name": "plugin-c",
603- // "dependencies": []'
604- // }
605- // ]
606- // }
607- //
608- // This means, `plugin-a` depends on `plugin-b` and `plugin-c`.
609- // `plugin-b` depends on `plugin-c`.
610- // `plugin-c` doesn't depend on anything.
611- File pluginsDependencyFile = new File (project. projectDir. parentFile. parentFile, ' .flutter-plugins-dependencies' )
612- if (pluginsDependencyFile. exists()) {
613- def object = new JsonSlurper (). parseText(pluginsDependencyFile. text)
614- assert(object instanceof Map )
615- assert(object. dependencyGraph instanceof List )
616- return object. dependencyGraph
617- }
618- return []
641+ private List<Map<String , Object > > getPluginDependencies (Project project ) {
642+ Map meta = project. ext. nativePluginLoader. getDependenciesMetadata(getFlutterSourceDirectory())
643+ if (meta == null ) {
644+ return []
645+ }
646+ assert(meta. dependencyGraph instanceof List<Map > )
647+ return meta. dependencyGraph as List<Map<String , Object > >
619648 }
620649
621650 private static String toCamelCase (List<String > parts ) {
@@ -1226,7 +1255,7 @@ class FlutterPlugin implements Plugin<Project> {
12261255 def nativeAssetsDir = " ${ project.buildDir} /../native_assets/android/jniLibs/lib/"
12271256 project. android. sourceSets. main. jniLibs. srcDir(nativeAssetsDir)
12281257 }
1229- configurePlugins()
1258+ configurePlugins(project )
12301259 detectLowCompileSdkVersionOrNdkVersion()
12311260 return
12321261 }
@@ -1278,7 +1307,7 @@ class FlutterPlugin implements Plugin<Project> {
12781307 }
12791308 }
12801309 }
1281- configurePlugins()
1310+ configurePlugins(project )
12821311 detectLowCompileSdkVersionOrNdkVersion()
12831312 }
12841313
0 commit comments