mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> Converted more null checks to null aware elements. The analyzer had many false negatives and the regex became pretty wild to find many of them 😂 I've found more in the engine folder but I'll apply the changes in a separate PR Part of #172188 *If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
660 lines
20 KiB
Dart
660 lines
20 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'package:yaml/yaml.dart';
|
|
|
|
import 'base/common.dart';
|
|
import 'base/file_system.dart';
|
|
|
|
/// Constant for 'pluginClass' key in plugin maps.
|
|
const kPluginClass = 'pluginClass';
|
|
|
|
/// Constant for 'dartPluginClass' key in plugin maps.
|
|
const kDartPluginClass = 'dartPluginClass';
|
|
|
|
/// Constant for 'dartPluginFile' key in plugin maps.
|
|
const kDartFileName = 'dartFileName';
|
|
|
|
/// Constant for 'ffiPlugin' key in plugin maps.
|
|
const kFfiPlugin = 'ffiPlugin';
|
|
|
|
// Constant for 'defaultPackage' key in plugin maps.
|
|
const kDefaultPackage = 'default_package';
|
|
|
|
/// Constant for 'sharedDarwinSource' key in plugin maps.
|
|
/// Can be set for iOS and macOS plugins.
|
|
const kSharedDarwinSource = 'sharedDarwinSource';
|
|
|
|
/// Constant for 'supportedVariants' key in plugin maps.
|
|
const kSupportedVariants = 'supportedVariants';
|
|
|
|
/// Platform variants that a Windows plugin can support.
|
|
enum PluginPlatformVariant {
|
|
/// Win32 variant of Windows.
|
|
win32,
|
|
}
|
|
|
|
/// Marker interface for all platform specific plugin config implementations.
|
|
abstract class PluginPlatform {
|
|
const PluginPlatform();
|
|
|
|
Map<String, dynamic> toMap();
|
|
}
|
|
|
|
/// A plugin that has platform variants.
|
|
abstract class VariantPlatformPlugin {
|
|
/// The platform variants supported by the plugin.
|
|
Set<PluginPlatformVariant> get supportedVariants;
|
|
}
|
|
|
|
abstract class NativeOrDartPlugin {
|
|
/// Determines whether the plugin has a Dart implementation.
|
|
bool hasDart();
|
|
|
|
/// Determines whether the plugin has a FFI implementation.
|
|
bool hasFfi();
|
|
|
|
/// Determines whether the plugin has a method channel implementation.
|
|
bool hasMethodChannel();
|
|
}
|
|
|
|
abstract class DarwinPlugin {
|
|
/// Indicates the iOS and macOS native code is shareable the subdirectory "darwin",
|
|
bool get sharedDarwinSource;
|
|
}
|
|
|
|
/// Contains parameters to template an Android plugin.
|
|
///
|
|
/// The [name] of the plugin is required. Additionally, either:
|
|
/// - [defaultPackage], or
|
|
/// - an implementation consisting of:
|
|
/// - the [package] and [pluginClass] that will be the entry point to the
|
|
/// plugin's native code, and/or
|
|
/// - the [dartPluginClass] with optional [dartFileName] that will be
|
|
/// the entry point for the plugin's Dart code
|
|
/// is required.
|
|
class AndroidPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|
AndroidPlugin({
|
|
required this.name,
|
|
required this.pluginPath,
|
|
this.package,
|
|
this.pluginClass,
|
|
this.dartPluginClass,
|
|
this.dartFileName,
|
|
bool? ffiPlugin,
|
|
this.defaultPackage,
|
|
required FileSystem fileSystem,
|
|
}) : _fileSystem = fileSystem,
|
|
ffiPlugin = ffiPlugin ?? false;
|
|
|
|
factory AndroidPlugin.fromYaml(
|
|
String name,
|
|
YamlMap yaml,
|
|
String pluginPath,
|
|
FileSystem fileSystem,
|
|
) {
|
|
assert(validate(yaml));
|
|
|
|
final dartPluginClass = yaml[kDartPluginClass] as String?;
|
|
final dartFileName = yaml[kDartFileName] as String?;
|
|
|
|
if (dartPluginClass == null && dartFileName != null) {
|
|
throwToolExit(
|
|
'"dartFileName" cannot be specified without "dartPluginClass" in Android platform of plugin "$name"',
|
|
);
|
|
}
|
|
|
|
return AndroidPlugin(
|
|
name: name,
|
|
package: yaml['package'] as String?,
|
|
pluginClass: yaml[kPluginClass] as String?,
|
|
dartPluginClass: dartPluginClass,
|
|
dartFileName: dartFileName,
|
|
ffiPlugin: yaml[kFfiPlugin] as bool? ?? false,
|
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
|
pluginPath: pluginPath,
|
|
fileSystem: fileSystem,
|
|
);
|
|
}
|
|
|
|
final FileSystem _fileSystem;
|
|
|
|
@override
|
|
bool hasMethodChannel() => pluginClass != null;
|
|
|
|
@override
|
|
bool hasFfi() => ffiPlugin;
|
|
|
|
@override
|
|
bool hasDart() => dartPluginClass != null;
|
|
|
|
static bool validate(YamlMap yaml) {
|
|
return (yaml['package'] is String && yaml[kPluginClass] is String) ||
|
|
yaml[kDartPluginClass] is String ||
|
|
yaml[kFfiPlugin] == true ||
|
|
yaml[kDefaultPackage] is String;
|
|
}
|
|
|
|
static const kConfigKey = 'android';
|
|
|
|
/// The plugin name defined in pubspec.yaml.
|
|
final String name;
|
|
|
|
/// The plugin package name defined in pubspec.yaml.
|
|
final String? package;
|
|
|
|
/// The native plugin main class defined in pubspec.yaml, if any.
|
|
final String? pluginClass;
|
|
|
|
/// The Dart plugin main class defined in pubspec.yaml, if any.
|
|
final String? dartPluginClass;
|
|
|
|
/// Path to file in which dartPluginClass defined, if any.
|
|
final String? dartFileName;
|
|
|
|
/// Is FFI plugin defined in pubspec.yaml.
|
|
final bool ffiPlugin;
|
|
|
|
/// The default implementation package defined in pubspec.yaml, if any.
|
|
final String? defaultPackage;
|
|
|
|
/// The absolute path to the plugin in the pub cache.
|
|
final String pluginPath;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{
|
|
'name': name,
|
|
'package': ?package,
|
|
'class': ?pluginClass,
|
|
kDartPluginClass: ?dartPluginClass,
|
|
kDartFileName: ?dartFileName,
|
|
if (ffiPlugin) kFfiPlugin: true,
|
|
kDefaultPackage: ?defaultPackage,
|
|
// Mustache doesn't support complex types.
|
|
'supportsEmbeddingV1': _supportedEmbeddings.contains('1'),
|
|
'supportsEmbeddingV2': _supportedEmbeddings.contains('2'),
|
|
};
|
|
}
|
|
|
|
/// Returns the version of the Android embedding.
|
|
late final Set<String> _supportedEmbeddings = _getSupportedEmbeddings();
|
|
|
|
Set<String> _getSupportedEmbeddings() {
|
|
final supportedEmbeddings = <String>{};
|
|
final String baseMainPath = _fileSystem.path.join(pluginPath, 'android', 'src', 'main');
|
|
|
|
final String? package = this.package;
|
|
// Don't attempt to validate the native code if there isn't supposed to
|
|
// be any.
|
|
if (package == null) {
|
|
return supportedEmbeddings;
|
|
}
|
|
|
|
final mainClassCandidates = <String>[
|
|
_fileSystem.path.join(
|
|
baseMainPath,
|
|
'java',
|
|
package.replaceAll('.', _fileSystem.path.separator),
|
|
'$pluginClass.java',
|
|
),
|
|
_fileSystem.path.join(
|
|
baseMainPath,
|
|
'kotlin',
|
|
package.replaceAll('.', _fileSystem.path.separator),
|
|
'$pluginClass.kt',
|
|
),
|
|
];
|
|
|
|
File? mainPluginClass;
|
|
var mainClassFound = false;
|
|
for (final mainClassCandidate in mainClassCandidates) {
|
|
mainPluginClass = _fileSystem.file(mainClassCandidate);
|
|
if (mainPluginClass.existsSync()) {
|
|
mainClassFound = true;
|
|
break;
|
|
}
|
|
}
|
|
if (mainPluginClass == null || !mainClassFound) {
|
|
assert(mainClassCandidates.length <= 2);
|
|
throwToolExit(
|
|
"The plugin `$name` doesn't have a main class defined in ${mainClassCandidates.join(' or ')}. "
|
|
"This is likely to due to an incorrect `androidPackage: $package` or `mainClass` entry in the plugin's pubspec.yaml.\n"
|
|
'If you are the author of this plugin, fix the `androidPackage` entry or move the main class to any of locations used above. '
|
|
'Otherwise, please contact the author of this plugin and consider using a different plugin in the meanwhile. ',
|
|
);
|
|
}
|
|
|
|
final String mainClassContent = mainPluginClass.readAsStringSync();
|
|
if (mainClassContent.contains('io.flutter.embedding.engine.plugins.FlutterPlugin')) {
|
|
supportedEmbeddings.add('2');
|
|
} else {
|
|
supportedEmbeddings.add('1');
|
|
}
|
|
if (mainClassContent.contains('PluginRegistry') && mainClassContent.contains('registerWith')) {
|
|
supportedEmbeddings.add('1');
|
|
}
|
|
return supportedEmbeddings;
|
|
}
|
|
}
|
|
|
|
/// Contains the parameters to template an iOS plugin.
|
|
///
|
|
/// The [name] of the plugin is required. Additionally, either:
|
|
/// - [defaultPackage], or
|
|
/// - an implementation consisting of:
|
|
/// - the [classPrefix] (with optional [pluginClass]) that will be the entry
|
|
/// point to the plugin's native code, and/or
|
|
/// - the [dartPluginClass] with optional [dartFileName] that will be
|
|
/// the entry point for the plugin's Dart code
|
|
/// is required.
|
|
class IOSPlugin extends PluginPlatform implements NativeOrDartPlugin, DarwinPlugin {
|
|
const IOSPlugin({
|
|
required this.name,
|
|
required this.classPrefix,
|
|
this.pluginClass,
|
|
this.dartPluginClass,
|
|
this.dartFileName,
|
|
bool? ffiPlugin,
|
|
this.defaultPackage,
|
|
bool? sharedDarwinSource,
|
|
}) : ffiPlugin = ffiPlugin ?? false,
|
|
sharedDarwinSource = sharedDarwinSource ?? false;
|
|
|
|
factory IOSPlugin.fromYaml(String name, YamlMap yaml) {
|
|
assert(validate(yaml)); // TODO(zanderso): https://github.com/flutter/flutter/issues/67241
|
|
|
|
final dartPluginClass = yaml[kDartPluginClass] as String?;
|
|
final dartFileName = yaml[kDartFileName] as String?;
|
|
|
|
if (dartPluginClass == null && dartFileName != null) {
|
|
throwToolExit(
|
|
'"dartFileName" cannot be specified without "dartPluginClass" in iOS platform of plugin "$name"',
|
|
);
|
|
}
|
|
|
|
return IOSPlugin(
|
|
name: name,
|
|
classPrefix: '',
|
|
pluginClass: yaml[kPluginClass] as String?,
|
|
dartPluginClass: dartPluginClass,
|
|
dartFileName: dartFileName,
|
|
ffiPlugin: yaml[kFfiPlugin] as bool? ?? false,
|
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
|
sharedDarwinSource: yaml[kSharedDarwinSource] as bool? ?? false,
|
|
);
|
|
}
|
|
|
|
static bool validate(YamlMap yaml) {
|
|
return yaml[kPluginClass] is String ||
|
|
yaml[kDartPluginClass] is String ||
|
|
yaml[kFfiPlugin] == true ||
|
|
yaml[kSharedDarwinSource] == true ||
|
|
yaml[kDefaultPackage] is String;
|
|
}
|
|
|
|
static const kConfigKey = 'ios';
|
|
|
|
final String name;
|
|
|
|
/// Note, this is here only for legacy reasons. Multi-platform format
|
|
/// always sets it to empty String.
|
|
final String classPrefix;
|
|
final String? pluginClass;
|
|
final String? dartPluginClass;
|
|
final String? dartFileName;
|
|
final bool ffiPlugin;
|
|
final String? defaultPackage;
|
|
|
|
/// Indicates the iOS native code is shareable with macOS in
|
|
/// the subdirectory "darwin", otherwise in the subdirectory "ios".
|
|
@override
|
|
final bool sharedDarwinSource;
|
|
|
|
@override
|
|
bool hasMethodChannel() => pluginClass != null;
|
|
|
|
@override
|
|
bool hasFfi() => ffiPlugin;
|
|
|
|
@override
|
|
bool hasDart() => dartPluginClass != null;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{
|
|
'name': name,
|
|
'prefix': classPrefix,
|
|
'class': ?pluginClass,
|
|
kDartPluginClass: ?dartPluginClass,
|
|
kDartFileName: ?dartFileName,
|
|
if (ffiPlugin) kFfiPlugin: true,
|
|
if (sharedDarwinSource) kSharedDarwinSource: true,
|
|
kDefaultPackage: ?defaultPackage,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Contains the parameters to template a macOS plugin.
|
|
///
|
|
/// The [name] of the plugin is required. Either [dartPluginClass] or
|
|
/// [pluginClass] or [ffiPlugin] are required.
|
|
/// [pluginClass] will be the entry point to the plugin's native code.
|
|
/// [dartFileName] is not required and will be used only if [dartPluginClass]
|
|
/// provided.
|
|
class MacOSPlugin extends PluginPlatform implements NativeOrDartPlugin, DarwinPlugin {
|
|
const MacOSPlugin({
|
|
required this.name,
|
|
this.pluginClass,
|
|
this.dartPluginClass,
|
|
this.dartFileName,
|
|
bool? ffiPlugin,
|
|
this.defaultPackage,
|
|
bool? sharedDarwinSource,
|
|
}) : ffiPlugin = ffiPlugin ?? false,
|
|
sharedDarwinSource = sharedDarwinSource ?? false;
|
|
|
|
factory MacOSPlugin.fromYaml(String name, YamlMap yaml) {
|
|
assert(validate(yaml));
|
|
|
|
final dartPluginClass = yaml[kDartPluginClass] as String?;
|
|
final dartFileName = yaml[kDartFileName] as String?;
|
|
|
|
if (dartPluginClass == null && dartFileName != null) {
|
|
throwToolExit(
|
|
'"dartFileName" cannot be specified without "dartPluginClass" in macOS platform of plugin "$name"',
|
|
);
|
|
}
|
|
|
|
return MacOSPlugin(
|
|
name: name,
|
|
pluginClass: yaml[kPluginClass] as String?,
|
|
dartPluginClass: dartPluginClass,
|
|
dartFileName: dartFileName,
|
|
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
|
sharedDarwinSource: yaml[kSharedDarwinSource] as bool?,
|
|
);
|
|
}
|
|
|
|
static bool validate(YamlMap yaml) {
|
|
return yaml[kPluginClass] is String ||
|
|
yaml[kDartPluginClass] is String ||
|
|
yaml[kFfiPlugin] == true ||
|
|
yaml[kSharedDarwinSource] == true ||
|
|
yaml[kDefaultPackage] is String;
|
|
}
|
|
|
|
static const kConfigKey = 'macos';
|
|
|
|
final String name;
|
|
final String? pluginClass;
|
|
final String? dartPluginClass;
|
|
final String? dartFileName;
|
|
final bool ffiPlugin;
|
|
final String? defaultPackage;
|
|
|
|
/// Indicates the macOS native code is shareable with iOS in
|
|
/// the subdirectory "darwin", otherwise in the subdirectory "macos".
|
|
@override
|
|
final bool sharedDarwinSource;
|
|
|
|
@override
|
|
bool hasMethodChannel() => pluginClass != null;
|
|
|
|
@override
|
|
bool hasFfi() => ffiPlugin;
|
|
|
|
@override
|
|
bool hasDart() => dartPluginClass != null;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{
|
|
'name': name,
|
|
'class': ?pluginClass,
|
|
kDartPluginClass: ?dartPluginClass,
|
|
kDartFileName: ?dartFileName,
|
|
if (ffiPlugin) kFfiPlugin: true,
|
|
if (sharedDarwinSource) kSharedDarwinSource: true,
|
|
kDefaultPackage: ?defaultPackage,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Contains the parameters to template a Windows plugin.
|
|
///
|
|
/// The [name] of the plugin is required. Either [dartPluginClass] or [pluginClass] are required.
|
|
/// [pluginClass] will be the entry point to the plugin's native code.
|
|
/// [dartFileName] is not required and will be used only if [dartPluginClass]
|
|
/// provided.
|
|
class WindowsPlugin extends PluginPlatform implements NativeOrDartPlugin, VariantPlatformPlugin {
|
|
const WindowsPlugin({
|
|
required this.name,
|
|
this.pluginClass,
|
|
this.dartPluginClass,
|
|
this.dartFileName,
|
|
bool? ffiPlugin,
|
|
this.defaultPackage,
|
|
this.variants = const <PluginPlatformVariant>{},
|
|
}) : ffiPlugin = ffiPlugin ?? false,
|
|
assert(pluginClass != null || dartPluginClass != null || defaultPackage != null);
|
|
|
|
factory WindowsPlugin.fromYaml(String name, YamlMap yaml) {
|
|
assert(validate(yaml));
|
|
final pluginClass = yaml[kPluginClass] as String?;
|
|
final variants = <PluginPlatformVariant>{};
|
|
final variantList = yaml[kSupportedVariants] as YamlList?;
|
|
if (variantList == null) {
|
|
// If no variant list is provided assume Win32 for backward compatibility.
|
|
variants.add(PluginPlatformVariant.win32);
|
|
} else {
|
|
const variantByName = <String, PluginPlatformVariant>{'win32': PluginPlatformVariant.win32};
|
|
for (final String variantName in variantList.cast<String>()) {
|
|
final PluginPlatformVariant? variant = variantByName[variantName];
|
|
if (variant != null) {
|
|
variants.add(variant);
|
|
}
|
|
// Ignore unrecognized variants to make adding new variants in the
|
|
// future non-breaking.
|
|
}
|
|
}
|
|
|
|
final dartPluginClass = yaml[kDartPluginClass] as String?;
|
|
final dartFileName = yaml[kDartFileName] as String?;
|
|
|
|
if (dartPluginClass == null && dartFileName != null) {
|
|
throwToolExit(
|
|
'"dartFileName" cannot be specified without "dartPluginClass" in Windows platform of plugin "$name"',
|
|
);
|
|
}
|
|
return WindowsPlugin(
|
|
name: name,
|
|
pluginClass: pluginClass,
|
|
dartPluginClass: dartPluginClass,
|
|
dartFileName: dartFileName,
|
|
ffiPlugin: yaml[kFfiPlugin] as bool?,
|
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
|
variants: variants,
|
|
);
|
|
}
|
|
|
|
static bool validate(YamlMap yaml) {
|
|
return yaml[kPluginClass] is String ||
|
|
yaml[kDartPluginClass] is String ||
|
|
yaml[kFfiPlugin] == true ||
|
|
yaml[kDefaultPackage] is String;
|
|
}
|
|
|
|
static const kConfigKey = 'windows';
|
|
|
|
final String name;
|
|
final String? pluginClass;
|
|
final String? dartPluginClass;
|
|
final String? dartFileName;
|
|
final bool ffiPlugin;
|
|
final String? defaultPackage;
|
|
final Set<PluginPlatformVariant> variants;
|
|
|
|
@override
|
|
Set<PluginPlatformVariant> get supportedVariants => variants;
|
|
|
|
@override
|
|
bool hasMethodChannel() => pluginClass != null;
|
|
|
|
@override
|
|
bool hasFfi() => ffiPlugin;
|
|
|
|
@override
|
|
bool hasDart() => dartPluginClass != null;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{
|
|
'name': name,
|
|
'class': ?pluginClass,
|
|
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass!),
|
|
kDartPluginClass: ?dartPluginClass,
|
|
kDartFileName: ?dartFileName,
|
|
if (ffiPlugin) kFfiPlugin: true,
|
|
kDefaultPackage: ?defaultPackage,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Contains the parameters to template a Linux plugin.
|
|
///
|
|
/// The [name] of the plugin is required. Either [dartPluginClass] or [pluginClass] are required.
|
|
/// [pluginClass] will be the entry point to the plugin's native code.
|
|
/// [dartFileName] is not required and will be used only if [dartPluginClass]
|
|
/// provided.
|
|
class LinuxPlugin extends PluginPlatform implements NativeOrDartPlugin {
|
|
const LinuxPlugin({
|
|
required this.name,
|
|
this.pluginClass,
|
|
this.dartPluginClass,
|
|
this.dartFileName,
|
|
bool? ffiPlugin,
|
|
this.defaultPackage,
|
|
}) : ffiPlugin = ffiPlugin ?? false,
|
|
assert(
|
|
pluginClass != null ||
|
|
dartPluginClass != null ||
|
|
(ffiPlugin ?? false) ||
|
|
defaultPackage != null,
|
|
);
|
|
|
|
factory LinuxPlugin.fromYaml(String name, YamlMap yaml) {
|
|
assert(validate(yaml));
|
|
|
|
final dartPluginClass = yaml[kDartPluginClass] as String?;
|
|
final dartFileName = yaml[kDartFileName] as String?;
|
|
|
|
if (dartPluginClass == null && dartFileName != null) {
|
|
throwToolExit(
|
|
'"dartFileName" cannot be specified without "dartPluginClass" in Linux platform of plugin "$name"',
|
|
);
|
|
}
|
|
|
|
return LinuxPlugin(
|
|
name: name,
|
|
pluginClass: yaml[kPluginClass] as String?,
|
|
dartPluginClass: dartPluginClass,
|
|
dartFileName: dartFileName,
|
|
ffiPlugin: yaml[kFfiPlugin] as bool? ?? false,
|
|
defaultPackage: yaml[kDefaultPackage] as String?,
|
|
);
|
|
}
|
|
|
|
static bool validate(YamlMap yaml) {
|
|
return yaml[kPluginClass] is String ||
|
|
yaml[kDartPluginClass] is String ||
|
|
yaml[kFfiPlugin] == true ||
|
|
yaml[kDefaultPackage] is String;
|
|
}
|
|
|
|
static const kConfigKey = 'linux';
|
|
|
|
final String name;
|
|
final String? pluginClass;
|
|
final String? dartPluginClass;
|
|
final String? dartFileName;
|
|
final bool ffiPlugin;
|
|
final String? defaultPackage;
|
|
|
|
@override
|
|
bool hasMethodChannel() => pluginClass != null;
|
|
|
|
@override
|
|
bool hasFfi() => ffiPlugin;
|
|
|
|
@override
|
|
bool hasDart() => dartPluginClass != null;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{
|
|
'name': name,
|
|
'class': ?pluginClass,
|
|
if (pluginClass != null) 'filename': _filenameForCppClass(pluginClass!),
|
|
kDartPluginClass: ?dartPluginClass,
|
|
kDartFileName: ?dartFileName,
|
|
if (ffiPlugin) kFfiPlugin: true,
|
|
kDefaultPackage: ?defaultPackage,
|
|
};
|
|
}
|
|
}
|
|
|
|
/// Contains the parameters to template a web plugin.
|
|
///
|
|
/// The required fields include: [name] of the plugin, the [pluginClass] that will
|
|
/// be the entry point to the plugin's implementation, and the [fileName]
|
|
/// containing the code.
|
|
class WebPlugin extends PluginPlatform {
|
|
const WebPlugin({required this.name, required this.pluginClass, required this.fileName});
|
|
|
|
factory WebPlugin.fromYaml(String name, YamlMap yaml) {
|
|
if (yaml['pluginClass'] is! String) {
|
|
throwToolExit(
|
|
'The plugin `$name` is missing the required field `pluginClass` in pubspec.yaml',
|
|
);
|
|
}
|
|
if (yaml['fileName'] is! String) {
|
|
throwToolExit('The plugin `$name` is missing the required field `fileName` in pubspec.yaml');
|
|
}
|
|
return WebPlugin(
|
|
name: name,
|
|
pluginClass: yaml['pluginClass'] as String,
|
|
fileName: yaml['fileName'] as String,
|
|
);
|
|
}
|
|
|
|
static const kConfigKey = 'web';
|
|
|
|
/// The name of the plugin.
|
|
final String name;
|
|
|
|
/// The class containing the plugin implementation details.
|
|
///
|
|
/// This class should have a static `registerWith` method defined.
|
|
final String pluginClass;
|
|
|
|
/// The name of the file containing the class implementation above.
|
|
final String fileName;
|
|
|
|
@override
|
|
Map<String, dynamic> toMap() {
|
|
return <String, dynamic>{'name': name, 'class': pluginClass, 'file': fileName};
|
|
}
|
|
}
|
|
|
|
final _internalCapitalLetterRegex = RegExp(r'(?=(?!^)[A-Z])');
|
|
String _filenameForCppClass(String className) {
|
|
return className.splitMapJoin(
|
|
_internalCapitalLetterRegex,
|
|
onMatch: (_) => '_',
|
|
onNonMatch: (String n) => n.toLowerCase(),
|
|
);
|
|
}
|