@@ -1191,13 +1191,13 @@ bool hasPlugins(FlutterProject project) {
11911191
11921192/// Resolves the platform implementation for Dart-only plugins.
11931193///
1194- /// * If there are multiple direct pub dependencies on packages that implement the
1195- /// frontend plugin for the current platform, fail .
1194+ /// * If there is only one dependency on a package that implements the
1195+ /// frontend plugin for the current platform, use that .
11961196/// * If there is a single direct dependency on a package that implements the
1197- /// frontend plugin for the target platform, this package is the selected implementation .
1198- /// * If there is no direct dependency on a package that implements the frontend
1199- /// plugin for the target platform, and the frontend plugin has a default implementation
1200- /// for the target platform the default implementation is selected .
1197+ /// frontend plugin for the current platform, use that .
1198+ /// * If there is no direct dependency on a package that implements the
1199+ /// frontend plugin, but there is a default for the current platform,
1200+ /// use that .
12011201/// * Else fail.
12021202///
12031203/// For more details, https://flutter.dev/go/federated-plugins.
@@ -1214,11 +1214,15 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
12141214 MacOSPlugin .kConfigKey,
12151215 WindowsPlugin .kConfigKey,
12161216 ];
1217- final Map <String , PluginInterfaceResolution > directDependencyResolutions
1218- = < String , PluginInterfaceResolution > {};
1217+ final Map <String , List < PluginInterfaceResolution >> possibleResolutions
1218+ = < String , List < PluginInterfaceResolution > > {};
12191219 final Map <String , String > defaultImplementations = < String , String > {};
1220- bool didFindError = false ;
1220+ // Generates a key for the maps above.
1221+ String getResolutionKey ({required String platform, required String packageName}) {
1222+ return '$packageName :$platform ' ;
1223+ }
12211224
1225+ bool hasPubspecError = false ;
12221226 for (final Plugin plugin in plugins) {
12231227 for (final String platform in platforms) {
12241228 if (plugin.platforms[platform] == null &&
@@ -1257,11 +1261,12 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
12571261 '\n '
12581262 );
12591263 }
1260- didFindError = true ;
1264+ hasPubspecError = true ;
12611265 continue ;
12621266 }
1267+ final String defaultImplementationKey = getResolutionKey (platform: platform, packageName: plugin.name);
12631268 if (defaultImplementation != null ) {
1264- defaultImplementations['$ platform /${ plugin . name }' ] = defaultImplementation;
1269+ defaultImplementations[defaultImplementationKey ] = defaultImplementation;
12651270 continue ;
12661271 } else {
12671272 // An app-facing package (i.e., one with no 'implements') with an
@@ -1281,52 +1286,87 @@ List<PluginInterfaceResolution> resolvePlatformImplementation(
12811286 minFlutterVersion.compareTo (semver.Version (2 , 11 , 0 )) >= 0 ;
12821287 if (! isDesktop || hasMinVersionForImplementsRequirement) {
12831288 implementsPackage = plugin.name;
1284- defaultImplementations['$platform /${plugin .name }' ] = plugin.name;
1289+ defaultImplementations[defaultImplementationKey] = plugin.name;
1290+ } else {
1291+ // If it doesn't meet any of the conditions, it isn't eligible for
1292+ // auto-registration.
1293+ continue ;
12851294 }
12861295 }
12871296 }
1297+ // If there's no Dart implementation, there's nothing to register.
12881298 if (plugin.pluginDartClassPlatforms[platform] == null ||
12891299 plugin.pluginDartClassPlatforms[platform] == 'none' ) {
12901300 continue ;
12911301 }
1292- final String resolutionKey = '$platform /$implementsPackage ' ;
1293- if (directDependencyResolutions.containsKey (resolutionKey)) {
1294- final PluginInterfaceResolution ? currResolution = directDependencyResolutions[resolutionKey];
1295- if (currResolution != null && currResolution.plugin.isDirectDependency) {
1296- if (plugin.isDirectDependency) {
1297- if (throwOnPluginPubspecError) {
1298- globals.printError (
1299- 'Plugin `${plugin .name }` implements an interface for `$platform `, which was already '
1300- 'implemented by plugin `${currResolution .plugin .name }`.\n '
1301- 'To fix this issue, remove either dependency from pubspec.yaml.'
1302- '\n\n '
1303- );
1304- }
1305- didFindError = true ;
1306- }
1307- // Use the plugin implementation added by the user as a direct dependency.
1308- continue ;
1309- }
1302+
1303+ // If it hasn't been skipped, it's a candidate for auto-registration, so
1304+ // add it as a possible resolution.
1305+ final String resolutionKey = getResolutionKey (platform: platform, packageName: implementsPackage);
1306+ if (! possibleResolutions.containsKey (resolutionKey)) {
1307+ possibleResolutions[resolutionKey] = < PluginInterfaceResolution > [];
13101308 }
1311- directDependencyResolutions [resolutionKey] = PluginInterfaceResolution (
1309+ possibleResolutions [resolutionKey]! . add ( PluginInterfaceResolution (
13121310 plugin: plugin,
13131311 platform: platform,
1314- );
1312+ )) ;
13151313 }
13161314 }
1317- if (didFindError && throwOnPluginPubspecError) {
1315+ if (hasPubspecError && throwOnPluginPubspecError) {
13181316 throwToolExit ('Please resolve the errors' );
13191317 }
1318+
1319+ // Now resolve all the possible resolutions to a single option for each
1320+ // plugin, or throw if that's not possible.
1321+ bool hasResolutionError = false ;
13201322 final List <PluginInterfaceResolution > finalResolution = < PluginInterfaceResolution > [];
1321- for (final MapEntry <String , PluginInterfaceResolution > resolution in directDependencyResolutions.entries) {
1322- if (resolution.value.plugin.isDirectDependency) {
1323- finalResolution.add (resolution.value);
1324- } else if (defaultImplementations.containsKey (resolution.key)) {
1325- // Pick the default implementation.
1326- if (defaultImplementations[resolution.key] == resolution.value.plugin.name) {
1327- finalResolution.add (resolution.value);
1323+ for (final MapEntry <String , List <PluginInterfaceResolution >> entry in possibleResolutions.entries) {
1324+ final List <PluginInterfaceResolution > candidates = entry.value;
1325+ // If there's only one candidate, use it.
1326+ if (candidates.length == 1 ) {
1327+ finalResolution.add (candidates.first);
1328+ continue ;
1329+ }
1330+ // Next, try direct dependencies of the resolving application.
1331+ final Iterable <PluginInterfaceResolution > directDependencies = candidates.where ((PluginInterfaceResolution r) {
1332+ return r.plugin.isDirectDependency;
1333+ });
1334+ if (directDependencies.isNotEmpty) {
1335+ if (directDependencies.length > 1 ) {
1336+ globals.printError (
1337+ 'Plugin ${entry .key } has conflicting direct dependency implementations:\n '
1338+ '${directDependencies .map ((PluginInterfaceResolution r ) => ' ${r .plugin .name }\n ' ).join ()}'
1339+ 'To fix this issue, remove all but one of these dependencies from pubspec.yaml.\n '
1340+ );
1341+ hasResolutionError = true ;
1342+ } else {
1343+ finalResolution.add (directDependencies.first);
1344+ }
1345+ continue ;
1346+ }
1347+ // Next, defer to the default implementation if there is one.
1348+ final String ? defaultPackageName = defaultImplementations[entry.key];
1349+ if (defaultPackageName != null ) {
1350+ final int defaultIndex = candidates
1351+ .indexWhere ((PluginInterfaceResolution r) => r.plugin.name == defaultPackageName);
1352+ if (defaultIndex != - 1 ) {
1353+ finalResolution.add (candidates[defaultIndex]);
1354+ continue ;
13281355 }
13291356 }
1357+ // Otherwise, require an explicit choice.
1358+ if (candidates.length > 1 ) {
1359+ globals.printError (
1360+ 'Plugin ${entry .key } has multiple possible implementations:\n '
1361+ '${candidates .map ((PluginInterfaceResolution r ) => ' ${r .plugin .name }\n ' ).join ()}'
1362+ 'To fix this issue, add one of these dependencies to pubspec.yaml.\n '
1363+ );
1364+ hasResolutionError = true ;
1365+ continue ;
1366+ }
1367+ }
1368+ if (hasResolutionError) {
1369+ throwToolExit ('Please resolve the errors' );
13301370 }
13311371 return finalResolution;
13321372}
0 commit comments