Skip to content

Commit 62116f9

Browse files
Improve Dart plugin registration handling (#122046)
Improve Dart plugin registration handling
1 parent 7e000b2 commit 62116f9

3 files changed

Lines changed: 248 additions & 113 deletions

File tree

packages/flutter_tools/lib/src/flutter_plugins.dart

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

packages/flutter_tools/lib/src/plugins.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,9 @@ class PluginInterfaceResolution {
418418
'dartClass': plugin.pluginDartClassPlatforms[platform] ?? '',
419419
};
420420
}
421+
422+
@override
423+
String toString() {
424+
return '<PluginInterfaceResolution ${plugin.name} for $platform>';
425+
}
421426
}

0 commit comments

Comments
 (0)