Skip to content

Commit e012752

Browse files
[flutter_tools] Don't generate native registrant classes if no pluginClass is defined (flutter#53785)
1 parent 63f8b9a commit e012752

File tree

5 files changed

+203
-33
lines changed

5 files changed

+203
-33
lines changed

packages/flutter_tools/lib/src/platform_plugins.dart

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,25 @@ import 'base/common.dart';
99
import 'base/file_system.dart';
1010
import 'globals.dart' as globals;
1111

12+
/// Constant for 'pluginClass' key in plugin maps.
13+
const String kPluginClass = 'pluginClass';
14+
15+
/// Constant for 'pluginClass' key in plugin maps.
16+
const String kDartPluginClass = 'dartPluginClass';
17+
1218
/// Marker interface for all platform specific plugin config impls.
1319
abstract class PluginPlatform {
1420
const PluginPlatform();
1521

1622
Map<String, dynamic> toMap();
1723
}
1824

25+
abstract class NativeOrDartPlugin {
26+
/// Determines whether the plugin has a native implementation or if it's a
27+
/// Dart-only plugin.
28+
bool isNative();
29+
}
30+
1931
/// Contains parameters to template an Android plugin.
2032
///
2133
/// The required fields include: [name] of the plugin, [package] of the plugin and
@@ -191,119 +203,140 @@ class IOSPlugin extends PluginPlatform {
191203

192204
/// Contains the parameters to template a macOS plugin.
193205
///
194-
/// The required fields include: [name] of the plugin, and [pluginClass] that will
195-
/// be the entry point to the plugin's native code.
196-
class MacOSPlugin extends PluginPlatform {
206+
/// The [name] of the plugin is required. Either [dartPluginClass] or [pluginClass] are required.
207+
/// [pluginClass] will be the entry point to the plugin's native code.
208+
class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin {
197209
const MacOSPlugin({
198210
@required this.name,
199-
@required this.pluginClass,
211+
this.pluginClass,
212+
this.dartPluginClass,
200213
});
201214

202215
factory MacOSPlugin.fromYaml(String name, YamlMap yaml) {
203216
assert(validate(yaml));
204217
return MacOSPlugin(
205218
name: name,
206-
pluginClass: yaml['pluginClass'] as String,
219+
pluginClass: yaml[kPluginClass] as String,
220+
dartPluginClass: yaml[kDartPluginClass] as String,
207221
);
208222
}
209223

210224
static bool validate(YamlMap yaml) {
211225
if (yaml == null) {
212226
return false;
213227
}
214-
return yaml['pluginClass'] is String;
228+
return yaml[kPluginClass] is String || yaml[kDartPluginClass] is String;
215229
}
216230

217231
static const String kConfigKey = 'macos';
218232

219233
final String name;
220234
final String pluginClass;
235+
final String dartPluginClass;
236+
237+
@override
238+
bool isNative() => pluginClass != null;
221239

222240
@override
223241
Map<String, dynamic> toMap() {
224242
return <String, dynamic>{
225243
'name': name,
226-
'class': pluginClass,
244+
if (pluginClass != null) 'class': pluginClass,
245+
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
227246
};
228247
}
229248
}
230249

231250
/// Contains the parameters to template a Windows plugin.
232251
///
233-
/// The required fields include: [name] of the plugin, and [pluginClass] that will
234-
/// be the entry point to the plugin's native code.
235-
class WindowsPlugin extends PluginPlatform {
252+
/// The [name] of the plugin is required. Either [dartPluginClass] or [pluginClass] are required.
253+
/// [pluginClass] will be the entry point to the plugin's native code.
254+
class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin{
236255
const WindowsPlugin({
237256
@required this.name,
238-
@required this.pluginClass,
239-
});
257+
this.pluginClass,
258+
this.dartPluginClass,
259+
}) : assert(pluginClass != null || dartPluginClass != null);
240260

241261
factory WindowsPlugin.fromYaml(String name, YamlMap yaml) {
242262
assert(validate(yaml));
243263
return WindowsPlugin(
244264
name: name,
245-
pluginClass: yaml['pluginClass'] as String,
265+
pluginClass: yaml[kPluginClass] as String,
266+
dartPluginClass: yaml[kDartPluginClass] as String,
246267
);
247268
}
248269

249270
static bool validate(YamlMap yaml) {
250271
if (yaml == null) {
251272
return false;
252273
}
253-
return yaml['pluginClass'] is String;
274+
return yaml[kDartPluginClass] is String || yaml[kPluginClass] is String;
254275
}
255276

256277
static const String kConfigKey = 'windows';
257278

258279
final String name;
259280
final String pluginClass;
281+
final String dartPluginClass;
282+
283+
@override
284+
bool isNative() => pluginClass != null;
260285

261286
@override
262287
Map<String, dynamic> toMap() {
263288
return <String, dynamic>{
264289
'name': name,
265-
'class': pluginClass,
266-
'filename': _filenameForCppClass(pluginClass),
290+
if (pluginClass != null) 'class': pluginClass,
291+
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass),
292+
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
267293
};
268294
}
269295
}
270296

271297
/// Contains the parameters to template a Linux plugin.
272298
///
273-
/// The required fields include: [name] of the plugin, and [pluginClass] that will
274-
/// be the entry point to the plugin's native code.
275-
class LinuxPlugin extends PluginPlatform {
299+
/// The [name] of the plugin is required. Either [dartPluginClass] or [pluginClass] are required.
300+
/// [pluginClass] will be the entry point to the plugin's native code.
301+
class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
276302
const LinuxPlugin({
277303
@required this.name,
278-
@required this.pluginClass,
279-
});
304+
this.pluginClass,
305+
this.dartPluginClass,
306+
}) : assert(pluginClass != null || dartPluginClass != null);
280307

281308
factory LinuxPlugin.fromYaml(String name, YamlMap yaml) {
282309
assert(validate(yaml));
283310
return LinuxPlugin(
284311
name: name,
285-
pluginClass: yaml['pluginClass'] as String,
312+
pluginClass: yaml[kPluginClass] as String,
313+
dartPluginClass: yaml[kDartPluginClass] as String,
286314
);
287315
}
288316

289317
static bool validate(YamlMap yaml) {
290318
if (yaml == null) {
291319
return false;
292320
}
293-
return yaml['pluginClass'] is String;
321+
return yaml[kPluginClass] is String || yaml[kDartPluginClass] is String;
294322
}
295323

296324
static const String kConfigKey = 'linux';
297325

298326
final String name;
299327
final String pluginClass;
328+
final String dartPluginClass;
329+
330+
@override
331+
bool isNative() => pluginClass != null;
300332

301333
@override
302334
Map<String, dynamic> toMap() {
303335
return <String, dynamic>{
304336
'name': name,
305-
'class': pluginClass,
306-
'filename': _filenameForCppClass(pluginClass),
337+
if (pluginClass != null) 'class': pluginClass,
338+
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass),
339+
if (dartPluginClass != null) 'dartPluginClass': dartPluginClass,
307340
};
308341
}
309342
}

packages/flutter_tools/lib/src/plugins.dart

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,8 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
871871
}
872872

873873
Future<void> _writeLinuxPluginFiles(FlutterProject project, List<Plugin> plugins) async {
874-
final List<Map<String, dynamic>> linuxPlugins = _extractPlatformMaps(plugins, LinuxPlugin.kConfigKey);
874+
final List<Plugin>nativePlugins = _filterNativePlugins(plugins, LinuxPlugin.kConfigKey);
875+
final List<Map<String, dynamic>> linuxPlugins = _extractPlatformMaps(nativePlugins, LinuxPlugin.kConfigKey);
875876
// The generated makefile is checked in, so can't use absolute paths. It is
876877
// included by the main makefile, so relative paths must be relative to that
877878
// file's directory.
@@ -901,7 +902,8 @@ Future<void> _writeLinuxPluginMakefile(Directory destination, Map<String, dynami
901902
}
902903

903904
Future<void> _writeMacOSPluginRegistrant(FlutterProject project, List<Plugin> plugins) async {
904-
final List<Map<String, dynamic>> macosPlugins = _extractPlatformMaps(plugins, MacOSPlugin.kConfigKey);
905+
final List<Plugin>nativePlugins = _filterNativePlugins(plugins, MacOSPlugin.kConfigKey);
906+
final List<Map<String, dynamic>> macosPlugins = _extractPlatformMaps(nativePlugins, MacOSPlugin.kConfigKey);
905907
final Map<String, dynamic> context = <String, dynamic>{
906908
'os': 'macos',
907909
'framework': 'FlutterMacOS',
@@ -915,8 +917,25 @@ Future<void> _writeMacOSPluginRegistrant(FlutterProject project, List<Plugin> pl
915917
);
916918
}
917919

920+
/// Filters out Dart-only plugins, which shouldn't be added to the native generated registrants.
921+
List<Plugin> _filterNativePlugins(List<Plugin> plugins, String platformKey) {
922+
return plugins.where((Plugin element) {
923+
final PluginPlatform plugin = element.platforms[platformKey];
924+
if (plugin == null) {
925+
return false;
926+
}
927+
if (plugin is NativeOrDartPlugin) {
928+
return (plugin as NativeOrDartPlugin).isNative();
929+
}
930+
// Not all platforms have the ability to create Dart-only plugins. Therefore, any plugin that doesn't
931+
// implement NativeOrDartPlugin is always native.
932+
return true;
933+
}).toList();
934+
}
935+
918936
Future<void> _writeWindowsPluginFiles(FlutterProject project, List<Plugin> plugins) async {
919-
final List<Map<String, dynamic>> windowsPlugins = _extractPlatformMaps(plugins, WindowsPlugin.kConfigKey);
937+
final List<Plugin>nativePlugins = _filterNativePlugins(plugins, WindowsPlugin.kConfigKey);
938+
final List<Map<String, dynamic>> windowsPlugins = _extractPlatformMaps(nativePlugins, WindowsPlugin.kConfigKey);
920939
final Map<String, dynamic> context = <String, dynamic>{
921940
'plugins': windowsPlugins,
922941
};
@@ -1092,7 +1111,9 @@ Future<void> injectPlugins(FlutterProject project, {bool checkProjects = false})
10921111
}
10931112
if (featureFlags.isWindowsEnabled && project.windows.existsSync()) {
10941113
await _writeWindowsPluginFiles(project, plugins);
1095-
await VisualStudioSolutionUtils(project: project.windows, fileSystem: globals.fs).updatePlugins(plugins);
1114+
1115+
final List<Plugin>nativePlugins = _filterNativePlugins(plugins, WindowsPlugin.kConfigKey);
1116+
await VisualStudioSolutionUtils(project: project.windows, fileSystem: globals.fs).updatePlugins(nativePlugins);
10961117
}
10971118
for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) {
10981119
if (!project.isModule && (!checkProjects || subproject.existsSync())) {

packages/flutter_tools/schema/pubspec_yaml.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,21 +80,24 @@
8080
"type": "object",
8181
"additionalProperties": false,
8282
"properties": {
83-
"pluginClass": {"type": "string"}
83+
"pluginClass": {"type": "string"},
84+
"dartPluginClass": {"type": "string"}
8485
}
8586
},
8687
"macos": {
8788
"type": "object",
8889
"additionalProperties": false,
8990
"properties": {
90-
"pluginClass": {"type": "string"}
91+
"pluginClass": {"type": "string"},
92+
"dartPluginClass": {"type": "string"}
9193
}
9294
},
9395
"windows": {
9496
"type": "object",
9597
"additionalProperties": false,
9698
"properties": {
97-
"pluginClass": {"type": "string"}
99+
"pluginClass": {"type": "string"},
100+
"dartPluginClass": {"type": "string"}
98101
}
99102
}
100103
}

packages/flutter_tools/test/general.shard/plugin_parsing_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,33 @@ void main() {
122122
expect(windowsPlugin.pluginClass, 'WinSamplePlugin');
123123
});
124124

125+
test('Allow for Dart-only plugins without a pluginClass', () {
126+
/// This is currently supported only on macOS, linux, Windows.
127+
const String pluginYamlRaw = 'implements: same_plugin\n' // this should be ignored by the tool
128+
'platforms:\n'
129+
' linux:\n'
130+
' dartPluginClass: LSamplePlugin\n'
131+
' macos:\n'
132+
' dartPluginClass: MSamplePlugin\n'
133+
' windows:\n'
134+
' dartPluginClass: WinSamplePlugin\n';
135+
136+
final dynamic pluginYaml = loadYaml(pluginYamlRaw);
137+
final Plugin plugin =
138+
Plugin.fromYaml(_kTestPluginName, _kTestPluginPath, pluginYaml as YamlMap, const <String>[]);
139+
140+
final LinuxPlugin linuxPlugin = plugin.platforms[LinuxPlugin.kConfigKey] as LinuxPlugin;
141+
final MacOSPlugin macOSPlugin = plugin.platforms[MacOSPlugin.kConfigKey] as MacOSPlugin;
142+
final WindowsPlugin windowsPlugin = plugin.platforms[WindowsPlugin.kConfigKey] as WindowsPlugin;
143+
144+
expect(linuxPlugin.pluginClass, isNull);
145+
expect(macOSPlugin.pluginClass, isNull);
146+
expect(windowsPlugin.pluginClass, isNull);
147+
expect(linuxPlugin.dartPluginClass, 'LSamplePlugin');
148+
expect(macOSPlugin.dartPluginClass, 'MSamplePlugin');
149+
expect(windowsPlugin.dartPluginClass, 'WinSamplePlugin');
150+
});
151+
125152
test('Legacy Format and Multi-Platform Format together is not allowed and error message contains plugin name', () {
126153
const String pluginYamlRaw = 'androidPackage: com.flutter.dev\n'
127154
'platforms:\n'

0 commit comments

Comments
 (0)