Add `--platforms` to `flutter create -t plugin` command (#59507)

diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index c5a36f6..b20b1e9 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -6,8 +6,9 @@
 
 import 'package:meta/meta.dart';
 import 'package:uuid/uuid.dart';
+import 'package:yaml/yaml.dart';
 
-import '../android/android.dart' as android;
+import '../android/android.dart' as android_common;
 import '../android/android_sdk.dart' as android_sdk;
 import '../android/gradle_utils.dart' as gradle;
 import '../base/common.dart';
@@ -15,20 +16,39 @@
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/net.dart';
+import '../base/terminal.dart';
 import '../base/utils.dart';
 import '../cache.dart';
 import '../convert.dart';
 import '../dart/pub.dart';
 import '../features.dart';
+import '../flutter_manifest.dart';
 import '../flutter_project_metadata.dart';
 import '../globals.dart' as globals;
+import '../plugins.dart';
 import '../project.dart';
 import '../reporting/reporting.dart';
 import '../runner/flutter_command.dart';
 import '../template.dart';
 
+const List<String> _kAvailablePlatforms = <String>[
+        'ios',
+        'android',
+        'windows',
+        'linux',
+        'macos',
+        'web',
+      ];
+
+const String _kNoPlatformsErrorMessage = '''
+The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
+To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
+directory. You can also find detailed instructions on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
+''';
+
 class CreateCommand extends FlutterCommand {
   CreateCommand() {
+    _addPlatformsOptions();
     argParser.addFlag('pub',
       defaultsTo: true,
       help: 'Whether to run "flutter pub get" after the project has been created.',
@@ -318,6 +338,19 @@
     final bool generatePlugin = template == FlutterProjectType.plugin;
     final bool generatePackage = template == FlutterProjectType.package;
 
+    final List<String> platforms = stringsArg('platforms');
+    // `--platforms` does not support module or package.
+    if (argResults.wasParsed('platforms') && (generateModule || generatePackage)) {
+      final String template = generateModule ? 'module' : 'package';
+      throwToolExit(
+        'The "--platforms" argument is not supported in $template template.',
+        exitCode: 2
+      );
+    } else if (platforms == null || platforms.isEmpty) {
+      throwToolExit('Must specify at least one platform using --platforms',
+        exitCode: 2);
+    }
+
     String organization = stringArg('org');
     if (!argResults.wasParsed('org')) {
       final FlutterProject project = FlutterProject.fromDirectory(projectDir);
@@ -344,7 +377,7 @@
       throwToolExit(error);
     }
 
-    final Map<String, dynamic> templateContext = _templateContext(
+    final Map<String, dynamic> templateContext = _createTemplateContext(
       organization: organization,
       projectName: projectName,
       projectDescription: stringArg('description'),
@@ -353,10 +386,12 @@
       withPluginHook: generatePlugin,
       androidLanguage: stringArg('android-language'),
       iosLanguage: stringArg('ios-language'),
-      web: featureFlags.isWebEnabled,
-      linux: featureFlags.isLinuxEnabled,
-      macos: featureFlags.isMacOSEnabled,
-      windows: featureFlags.isWindowsEnabled,
+      ios: platforms.contains('ios'),
+      android: platforms.contains('android'),
+      web: featureFlags.isWebEnabled && platforms.contains('web'),
+      linux: featureFlags.isLinuxEnabled && platforms.contains('linux'),
+      macos: featureFlags.isMacOSEnabled && platforms.contains('macos'),
+      windows: featureFlags.isWindowsEnabled && platforms.contains('windows'),
     );
 
     final String relativeDirPath = globals.fs.path.relative(projectDirPath);
@@ -417,7 +452,8 @@
       if (globals.doctor.canLaunchAnything) {
         // Let them know a summary of the state of their tooling.
         await globals.doctor.summary();
-
+        final List<String> platforms = _getSupportedPlatformsFromTemplateContext(templateContext);
+        final String platformsString = platforms.join(', ');
         globals.printStatus('''
 In order to run your $application, type:
 
@@ -430,7 +466,7 @@
           globals.printStatus('''
 Your plugin code is in $relativePluginMain.
 
-Host platform code is in the "android" and "ios" directories under $relativePluginPath.
+Host platform code is in the $platformsString directories under $relativePluginPath.
 To edit platform code in an IDE see https://flutter.dev/developing-packages/#edit-plugin-package.
 ''');
         }
@@ -467,6 +503,17 @@
     return FlutterCommandResult.success();
   }
 
+  void _addPlatformsOptions() {
+    argParser.addMultiOption('platforms',
+      help: 'the platforms supported by this project.'
+        'This argument only works when the --template is set to app or plugin.'
+        'Platform folders (e.g. android/) will be generated in the target project.'
+        'When adding platforms to a plugin project, the pubspec.yaml will be updated with the requested platform.'
+        'Adding desktop platforms requires the corresponding desktop config setting to be enabled.',
+      defaultsTo: _kAvailablePlatforms,
+      allowed: _kAvailablePlatforms);
+  }
+
   Future<int> _generateModule(Directory directory, Map<String, dynamic> templateContext, { bool overwrite = false }) async {
     int generatedCount = 0;
     final String description = argResults.wasParsed('description')
@@ -504,12 +551,27 @@
   }
 
   Future<int> _generatePlugin(Directory directory, Map<String, dynamic> templateContext, { bool overwrite = false }) async {
+    // Plugin doesn't create any platform by default
+    if (!argResults.wasParsed('platforms')) {
+      // If the user didn't explicitly declare the platforms, we don't generate any platforms.
+      templateContext['ios'] = false;
+      templateContext['android'] = false;
+      templateContext['web'] = false;
+      templateContext['linux'] = false;
+      templateContext['macos'] = false;
+      templateContext['windows'] = false;
+      globals.printError(_kNoPlatformsErrorMessage);
+    }
+    final List<String> platforms = _getSupportedPlatformsFromTemplateContext(templateContext);
+    final bool willAddPlatforms = platforms.isNotEmpty;
+    templateContext['no_platforms'] = !willAddPlatforms;
     int generatedCount = 0;
     final String description = argResults.wasParsed('description')
         ? stringArg('description')
         : 'A new flutter plugin project.';
     templateContext['description'] = description;
     generatedCount += await _renderTemplate('plugin', directory, templateContext, overwrite: overwrite);
+
     if (boolArg('pub')) {
       await pub.get(
         context: PubContext.createPlugin,
@@ -517,8 +579,45 @@
         offline: boolArg('offline'),
       );
     }
+
+    if (willAddPlatforms) {
+      // If adding new platforms to an existing plugin project, prints
+      // a help message containing the platforms maps need to be added to the `platforms` key in the pubspec.
+      final String pubspecPath = globals.fs.path.join(directory.absolute.path, 'pubspec.yaml');
+      final List<String> platformsToAdd = List<String>.from(platforms);
+      final FlutterManifest manifest = FlutterManifest.createFromPath(pubspecPath, fileSystem: globals.fs, logger: globals.logger);
+      final List<String> existingPlatforms = manifest.supportedPlatforms.keys.toList();
+      platformsToAdd.removeWhere(existingPlatforms.contains);
+      final YamlMap platformsMapToPrint = Plugin.createPlatformsYamlMap(platformsToAdd, templateContext['pluginClass'] as String, templateContext['androidIdentifier'] as String);
+      if (platformsMapToPrint.isNotEmpty) {
+        String prettyYaml = '';
+        for (final String platform in platformsMapToPrint.keys.toList().cast<String>()) {
+          prettyYaml += '$platform:\n';
+          for (final String key in (platformsMapToPrint[platform] as YamlMap).keys.toList().cast<String>()) {
+            prettyYaml += ' $key: ${platformsMapToPrint[platform][key] as String}\n';
+          }
+        }
+        globals.printStatus('''
+The `pubspec.yaml` under the project directory must be updated to support ${platformsToAdd.join(', ')},
+Add below lines to under the `platform:` key:
+''', emphasis: true);
+      globals.printStatus(prettyYaml, emphasis: true, color: TerminalColor.blue);
+      globals.printStatus('''
+If the `platforms` key does not exist in the `pubspec.yaml`, it might because that the plugin project does not
+use the multi-platforms plugin format. We highly recommend a migration to the multi-platforms plugin format.
+For detailed instructions on how to format the pubspec.yaml to support platforms using the multi-platforms format, see:
+https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms
+''', emphasis: true);
+      }
+    }
+
+
     final FlutterProject project = FlutterProject.fromDirectory(directory);
-    gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
+    final bool generateAndroid = templateContext['android'] == true;
+    if (generateAndroid) {
+      gradle.updateLocalProperties(
+        project: project, requireAndroidSdk: false);
+    }
 
     final String projectName = templateContext['projectName'] as String;
     final String organization = templateContext['organization'] as String;
@@ -527,19 +626,22 @@
     templateContext['projectName'] = exampleProjectName;
     templateContext['androidIdentifier'] = _createAndroidIdentifier(organization, exampleProjectName);
     templateContext['iosIdentifier'] = _createUTIIdentifier(organization, exampleProjectName);
+    templateContext['macosIdentifier'] = _createUTIIdentifier(organization, exampleProjectName);
     templateContext['description'] = 'Demonstrates how to use the $projectName plugin.';
     templateContext['pluginProjectName'] = projectName;
     templateContext['androidPluginIdentifier'] = androidPluginIdentifier;
 
-    generatedCount += await _generateApp(project.example.directory, templateContext, overwrite: overwrite);
+    generatedCount += await _generateApp(project.example.directory, templateContext, overwrite: overwrite, pluginExampleApp: true);
     return generatedCount;
   }
 
-  Future<int> _generateApp(Directory directory, Map<String, dynamic> templateContext, { bool overwrite = false }) async {
+  Future<int> _generateApp(Directory directory, Map<String, dynamic> templateContext, { bool overwrite = false, bool pluginExampleApp = false}) async {
     int generatedCount = 0;
     generatedCount += await _renderTemplate('app', directory, templateContext, overwrite: overwrite);
     final FlutterProject project = FlutterProject.fromDirectory(directory);
-    generatedCount += _injectGradleWrapper(project);
+    if (templateContext['android'] == true) {
+      generatedCount += _injectGradleWrapper(project);
+    }
 
     if (boolArg('with-driver-test')) {
       final Directory testDirectory = directory.childDirectory('test_driver');
@@ -548,11 +650,11 @@
 
     if (boolArg('pub')) {
       await pub.get(context: PubContext.create, directory: directory.path, offline: boolArg('offline'));
-      await project.ensureReadyForPlatformSpecificTooling(checkProjects: false);
+      await project.ensureReadyForPlatformSpecificTooling(checkProjects: pluginExampleApp);
     }
-
-    gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
-
+    if (templateContext['android'] == true) {
+      gradle.updateLocalProperties(project: project, requireAndroidSdk: false);
+    }
     return generatedCount;
   }
 
@@ -570,7 +672,24 @@
     return -files.length;
   }
 
-  Map<String, dynamic> _templateContext({
+  List<String> _getSupportedPlatformsFromTemplateContext(Map<String, dynamic> templateContext) {
+    return <String>[
+      if (templateContext['ios'] == true)
+        'ios',
+      if (templateContext['android'] == true)
+        'android',
+      if (templateContext['web'] == true)
+        'web',
+      if (templateContext['linux'] == true)
+        'linux',
+      if (templateContext['windows'] == true)
+        'windows',
+      if (templateContext['macos'] == true)
+        'macos',
+    ];
+  }
+
+  Map<String, dynamic> _createTemplateContext({
     String organization,
     String projectName,
     String projectDescription,
@@ -579,6 +698,8 @@
     String flutterRoot,
     bool renderDriverTest = false,
     bool withPluginHook = false,
+    bool ios = false,
+    bool android = false,
     bool web = false,
     bool linux = false,
     bool macos = false,
@@ -603,7 +724,7 @@
       'description': projectDescription,
       'dartSdk': '$flutterRoot/bin/cache/dart-sdk',
       'useAndroidEmbeddingV2': featureFlags.isAndroidEmbeddingV2Enabled,
-      'androidMinApiLevel': android.minApiLevel,
+      'androidMinApiLevel': android_common.minApiLevel,
       'androidSdkVersion': android_sdk.minimumAndroidSdkVersion,
       'withDriverTest': renderDriverTest,
       'pluginClass': pluginClass,
@@ -616,6 +737,8 @@
       'iosLanguage': iosLanguage,
       'flutterRevision': globals.flutterVersion.frameworkRevision,
       'flutterChannel': globals.flutterVersion.channel,
+      'ios': ios,
+      'android': android,
       'web': web,
       'linux': linux,
       'macos': macos,
diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart
index ec18de1..bc2d2cd 100644
--- a/packages/flutter_tools/lib/src/flutter_manifest.dart
+++ b/packages/flutter_tools/lib/src/flutter_manifest.dart
@@ -163,17 +163,16 @@
     if (isModule) {
       return _flutterDescriptor['module']['androidPackage'] as String;
     }
-    if (isPlugin) {
-      final YamlMap plugin = _flutterDescriptor['plugin'] as YamlMap;
-      if (plugin.containsKey('platforms')) {
-        final YamlMap platforms = plugin['platforms'] as YamlMap;
-
-        if (platforms.containsKey('android')) {
-          return platforms['android']['package'] as String;
-        }
-      } else {
+    if (supportedPlatforms == null) {
+      // Pre-multi-platform plugin format
+      if (isPlugin) {
+        final YamlMap plugin = _flutterDescriptor['plugin'] as YamlMap;
         return plugin['androidPackage'] as String;
       }
+      return null;
+    }
+    if (supportedPlatforms.containsKey('android')) {
+       return supportedPlatforms['android']['package'] as String;
     }
     return null;
   }
@@ -187,6 +186,20 @@
     return null;
   }
 
+  /// Gets the supported platforms. This only supports the new `platforms` format.
+  ///
+  /// If the plugin uses the legacy pubspec format, this method returns null.
+  Map<String, dynamic> get supportedPlatforms {
+    if (isPlugin) {
+      final YamlMap plugin = _flutterDescriptor['plugin'] as YamlMap;
+      if (plugin.containsKey('platforms')) {
+        final YamlMap platformsMap = plugin['platforms'] as YamlMap;
+        return platformsMap.value.cast<String, dynamic>();
+      }
+    }
+    return null;
+  }
+
   List<Map<String, dynamic>> get fontsDescriptor {
     return fonts.map((Font font) => font.descriptor).toList();
   }
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index dea67de..79d9305 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -174,6 +174,25 @@
     );
   }
 
+  /// Create a YamlMap that represents the supported platforms.
+  ///
+  /// For example, if the `platforms` contains 'ios' and 'android', the return map looks like:
+  ///    android:
+  ///      package: io.flutter.plugins.sample
+  ///      pluginClass: SamplePlugin
+  ///    ios:
+  ///      pluginClass: SamplePlugin
+  static YamlMap createPlatformsYamlMap(List<String> platforms, String pluginClass, String androidPackage) {
+    final Map<String, dynamic> map = <String, dynamic>{};
+    for (final String platform in platforms) {
+      map[platform] = <String, String>{
+        'pluginClass': pluginClass,
+        ...platform == 'android' ? <String, String>{'package': androidPackage} : <String, String>{},
+      };
+    }
+    return YamlMap.wrap(map);
+  }
+
   static List<String> validatePluginYaml(YamlMap yaml) {
     if (yaml == null) {
       return <String>['Invalid "plugin" specification.'];
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 2672d62..896a0fd 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -225,6 +225,9 @@
 
   /// Generates project files necessary to make Gradle builds work on Android
   /// and CocoaPods+Xcode work on iOS, for app and module projects only.
+  // TODO(cyanglaz): The param `checkProjects` is confusing. We should give it a better name
+  // or add some documentation explaining what it does, or both.
+  // https://github.com/flutter/flutter/issues/60023
   Future<void> ensureReadyForPlatformSpecificTooling({bool checkProjects = false}) async {
     if (!directory.existsSync() || hasExampleApp) {
       return;
@@ -648,6 +651,7 @@
     template.render(
       target,
       <String, dynamic>{
+        'ios': true,
         'projectName': parent.manifest.appName,
         'iosIdentifier': parent.manifest.iosBundleIdentifier,
       },
@@ -797,6 +801,7 @@
     template.render(
       target,
       <String, dynamic>{
+        'android': true,
         'projectName': parent.manifest.appName,
         'androidIdentifier': parent.manifest.androidPackage,
         'androidX': usesAndroidX,
diff --git a/packages/flutter_tools/lib/src/template.dart b/packages/flutter_tools/lib/src/template.dart
index 6945c43..d36353d 100644
--- a/packages/flutter_tools/lib/src/template.dart
+++ b/packages/flutter_tools/lib/src/template.dart
@@ -108,6 +108,16 @@
         }
         relativeDestinationPath = relativeDestinationPath.replaceAll('$platform-$language.tmpl', platform);
       }
+
+      final bool android = context['android'] as bool;
+      if (relativeDestinationPath.contains('android') && !android) {
+        return null;
+      }
+
+      // TODO(cyanglaz): do not add iOS folder by default when 1.20.0 is released.
+      // Also need to update the flutter SDK min constraint in the pubspec.yaml to 1.20.0.
+      // https://github.com/flutter/flutter/issues/59787
+
       // Only build a web project if explicitly asked.
       final bool web = context['web'] as bool;
       if (relativeDestinationPath.contains('web') && !web) {
@@ -128,6 +138,7 @@
       if (relativeDestinationPath.startsWith('windows.tmpl') && !windows) {
         return null;
       }
+
       final String projectName = context['projectName'] as String;
       final String androidIdentifier = context['androidIdentifier'] as String;
       final String pluginClass = context['pluginClass'] as String;
@@ -139,7 +150,7 @@
         .replaceAll(imageTemplateExtension, '')
         .replaceAll(templateExtension, '');
 
-      if (androidIdentifier != null) {
+      if (android != null && android && androidIdentifier != null) {
         finalDestinationPath = finalDestinationPath
             .replaceAll('androidIdentifier', androidIdentifier.replaceAll('.', pathSeparator));
       }
diff --git a/packages/flutter_tools/templates/plugin/README.md.tmpl b/packages/flutter_tools/templates/plugin/README.md.tmpl
index d9c79a1..fc3fa8f 100644
--- a/packages/flutter_tools/templates/plugin/README.md.tmpl
+++ b/packages/flutter_tools/templates/plugin/README.md.tmpl
@@ -9,6 +9,12 @@
 a specialized package that includes platform-specific implementation code for
 Android and/or iOS.
 
-For help getting started with Flutter, view our 
-[online documentation](https://flutter.dev/docs), which offers tutorials, 
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
 samples, guidance on mobile development, and a full API reference.
+
+{{#no_platforms}}
+The plugin project was generated without specifying the `--platforms` flag, no platforms are currently supported.
+To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
+directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
+{{/no_platforms}}
diff --git a/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl b/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl
index f7b9f56..5ccb0c4 100644
--- a/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl
+++ b/packages/flutter_tools/templates/plugin/lib/projectName.dart.tmpl
@@ -1,3 +1,10 @@
+{{#no_platforms}}
+// You have generated a new plugin project without
+// specifying the `--platforms` flag. A plugin project supports no platforms is generated.
+// To add platforms, run `flutter create -t plugin --platforms <platforms> .` under the same
+// directory. You can also find a detailed instruction on how to add platforms in the `pubspec.yaml` at https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms.
+{{/no_platforms}}
+
 import 'dart:async';
 
 import 'package:flutter/services.dart';
diff --git a/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl b/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl
index 1cc1ca49..fb93073 100644
--- a/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl
+++ b/packages/flutter_tools/templates/plugin/pubspec.yaml.tmpl
@@ -31,11 +31,25 @@
   # adding or updating assets for this project.
   plugin:
     platforms:
+{{#no_platforms}}
+    # This plugin project was generated without specifying any
+    # platforms with the `--platform` argument. If you see the `fake_platform` map below, remove it and
+    # then add platforms following the instruction here:
+    # https://flutter.dev/docs/development/packages-and-plugins/developing-packages#plugin-platforms
+    # -------------------
+      some_platform:
+        pluginClass: somePluginClass
+    # -------------------
+{{/no_platforms}}
+{{#android}}
       android:
         package: {{androidIdentifier}}
         pluginClass: {{pluginClass}}
+{{/android}}
+{{#ios}}
       ios:
         pluginClass: {{pluginClass}}
+{{/ios}}
 {{#linux}}
       linux:
         pluginClass: {{pluginClass}}
@@ -44,6 +58,10 @@
       macos:
         pluginClass: {{pluginClass}}
 {{/macos}}
+{{#windows}}
+      windows:
+        pluginClass: {{pluginClass}}
+{{/windows}}
 {{#web}}
       web:
         pluginClass: {{pluginDartClass}}Web
diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
index b856d69..014e70a 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
@@ -26,6 +26,7 @@
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/pubspec_schema.dart';
 import '../../src/testbed.dart';
 
 const String frameworkRevision = '12345678';
@@ -242,23 +243,18 @@
   testUsingContext('detects and recreates a plugin project correctly', () async {
     await projectDir.create(recursive: true);
     await projectDir.absolute.childFile('.metadata').writeAsString('project_type: plugin\n');
-    return _createAndAnalyzeProject(
+    await _createAndAnalyzeProject(
       projectDir,
+      <String>[],
       <String>[
-        '-i', 'objc', '-a', 'java',
-      ],
-      <String>[
-        'android/src/main/java/com/example/flutter_project/FlutterProjectPlugin.java',
-        'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',
-        'example/ios/Runner/AppDelegate.h',
-        'example/ios/Runner/AppDelegate.m',
-        'example/ios/Runner/main.m',
         'example/lib/main.dart',
         'flutter_project.iml',
-        'ios/Classes/FlutterProjectPlugin.h',
-        'ios/Classes/FlutterProjectPlugin.m',
         'lib/flutter_project.dart',
       ],
+      unexpectedPaths: <String>[
+        'android/app/src/main/java/com/example/flutter_project/MainActivity.java',
+        'android/src/main/java/com/example/flutter_project/FlutterProjectPlugin.java',
+        'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',]
     );
   }, overrides: <Type, Generator>{
     Pub: () => Pub(
@@ -380,19 +376,16 @@
       projectDir,
       <String>['--template=plugin', '-i', 'objc', '-a', 'java'],
       <String>[
-        'android/src/main/java/com/example/flutter_project/FlutterProjectPlugin.java',
-        'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',
-        'example/ios/Runner/AppDelegate.h',
-        'example/ios/Runner/AppDelegate.m',
-        'example/ios/Runner/main.m',
         'example/lib/main.dart',
         'flutter_project.iml',
-        'ios/Classes/FlutterProjectPlugin.h',
-        'ios/Classes/FlutterProjectPlugin.m',
         'lib/flutter_project.dart',
       ],
       unexpectedPaths: <String>[
+        'android/src/main/java/com/example/flutter_project/FlutterProjectPlugin.java',
+        'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',
         'lib/flutter_project_web.dart',
+        // TODO(cyanglaz): no-op iOS folder should be removed after 1.20.0 release
+        // https://github.com/flutter/flutter/issues/59787
       ],
     );
     return _runFlutterTest(projectDir.childDirectory('example'));
@@ -410,7 +403,7 @@
   testUsingContext('plugin project supports web', () async {
     await _createAndAnalyzeProject(
       projectDir,
-      <String>['--template=plugin'],
+      <String>['--template=plugin', '--platforms=web'],
       <String>[
         'lib/flutter_project.dart',
         'lib/flutter_project_web.dart',
@@ -464,7 +457,7 @@
   testUsingContext('kotlin/swift plugin project', () async {
     return _createProject(
       projectDir,
-      <String>['--no-pub', '--template=plugin', '-a', 'kotlin', '--ios-language', 'swift'],
+      <String>['--no-pub', '--template=plugin', '-a', 'kotlin', '--ios-language', 'swift', '--platforms', 'ios,android'],
       <String>[
         'android/src/main/kotlin/com/example/flutter_project/FlutterProjectPlugin.kt',
         'example/android/app/src/main/kotlin/com/example/flutter_project_example/MainActivity.kt',
@@ -495,6 +488,7 @@
         '--org', 'com.bar.foo',
         '-i', 'objc',
         '-a', 'java',
+        '--platforms', 'android',
       ], <String>[
         'android/src/main/java/com/bar/foo/flutter_project/FlutterProjectPlugin.java',
         'example/android/app/src/main/java/com/bar/foo/flutter_project_example/MainActivity.java',
@@ -515,6 +509,7 @@
         '--project-name', 'xyz',
         '-i', 'objc',
         '-a', 'java',
+        '--platforms', 'android,ios',
       ], <String>[
         'android/src/main/java/com/example/xyz/XyzPlugin.java',
         'example/android/app/src/main/java/com/example/xyz_example/MainActivity.java',
@@ -529,7 +524,7 @@
   testUsingContext('plugin project with invalid custom project name', () async {
     expect(
       () => _createProject(projectDir,
-        <String>['--no-pub', '--template=plugin', '--project-name', 'xyz.xyz'],
+        <String>['--no-pub', '--template=plugin', '--project-name', 'xyz.xyz', '--platforms', 'android,ios',],
         <String>[],
       ),
       throwsToolExit(message: '"xyz.xyz" is not a valid Dart package name.'),
@@ -642,7 +637,7 @@
     final CreateCommand command = CreateCommand();
     final CommandRunner<void> runner = createTestCommandRunner(command);
 
-    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms', 'android', projectDir.path]);
 
     void expectExists(String relPath) {
       expect(globals.fs.isFileSync('${projectDir.path}/$relPath'), true);
@@ -693,9 +688,14 @@
     final CreateCommand command = CreateCommand();
     final CommandRunner<void> runner = createTestCommandRunner(command);
 
-    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=linux', projectDir.path]);
 
     expect(projectDir.childDirectory('linux').childFile('CMakeLists.txt').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('linux').existsSync(), true);
+        validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'linux',
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform']);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
   });
@@ -711,6 +711,7 @@
     await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
 
     expect(projectDir.childDirectory('linux').childFile('CMakeLists.txt').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('linux').existsSync(), false);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
   });
@@ -738,9 +739,14 @@
     final CreateCommand command = CreateCommand();
     final CommandRunner<void> runner = createTestCommandRunner(command);
 
-    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=macos', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('macos').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'macos',
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform']);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
   });
@@ -756,6 +762,7 @@
     await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('macos').existsSync(), false);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
   });
@@ -798,9 +805,14 @@
     final CreateCommand command = CreateCommand();
     final CommandRunner<void> runner = createTestCommandRunner(command);
 
-    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=windows', projectDir.path]);
 
     expect(projectDir.childDirectory('windows').childFile('plugin.vcxproj').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('windows').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'windows'
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform']);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
   });
@@ -816,6 +828,7 @@
     await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
 
     expect(projectDir.childDirectory('windows').childFile('plugin.vcxproj').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('windows').existsSync(), false);
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
   });
@@ -1269,6 +1282,7 @@
         '--org', 'com.bar.foo',
         '-i', 'objc',
         '-a', 'java',
+        '--platforms', 'ios,android'
       ],
       <String>[],
     );
@@ -1276,7 +1290,7 @@
     projectDir.childDirectory('ios').deleteSync(recursive: true);
     await _createProject(
       projectDir,
-      <String>['--no-pub', '--template=plugin', '-i', 'objc', '-a', 'java'],
+      <String>['--no-pub', '--template=plugin', '-i', 'objc', '-a', 'java', '--platforms', 'ios,android'],
       <String>[
         'example/android/app/src/main/java/com/bar/foo/flutter_project_example/MainActivity.java',
         'ios/Classes/FlutterProjectPlugin.h',
@@ -1506,6 +1520,306 @@
     HttpClientFactory: () =>
         () => MockHttpClient(404, result: 'not found'),
   });
+
+  testUsingContext('plugin does not support any platform by default', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+
+    // TODO(cyanglaz): no-op iOS folder should be removed after 1.20.0 release
+    // https://github.com/flutter/flutter/issues/59787
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('android').existsSync(), false);
+    expect(projectDir.childDirectory('web').existsSync(), false);
+    expect(projectDir.childDirectory('linux').existsSync(), false);
+    expect(projectDir.childDirectory('windows').existsSync(), false);
+    expect(projectDir.childDirectory('macos').existsSync(), false);
+
+    // TODO(cyanglaz): no-op iOS folder should be removed after 1.20.0 release
+    // https://github.com/flutter/flutter/issues/59787
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('android').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('web').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('linux').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('windows').existsSync(), false);
+    expect(projectDir.childDirectory('example').childDirectory('macos').existsSync(), false);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: <String>[
+      'some_platform'
+    ], pluginClass: 'somePluginClass',
+    unexpectedPlatforms: <String>[ 'ios', 'android', 'web', 'linux', 'windows', 'macos']);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+
+  // TODO(cyanglaz): no-op iOS folder should be removed after 1.20.0 release
+  // https://github.com/flutter/flutter/issues/59787
+  testUsingContext('create an empty plugin contains a no-op ios folder, but no pubspec entry.', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'some_platform'
+    ], pluginClass: 'somePluginClass',
+    unexpectedPlatforms: <String>['ios']);
+  });
+
+  testUsingContext('plugin supports ios if requested', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=ios', projectDir.path]);
+
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: <String>[
+      'ios',
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform']);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+  testUsingContext('plugin supports android if requested', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=android', projectDir.path]);
+
+    expect(projectDir.childDirectory('android').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('android').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'android'
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform'],
+    androidIdentifier: 'com.example.flutter_project');
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+  testUsingContext('plugin supports web if requested', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=web', projectDir.path]);
+    expect(projectDir.childDirectory('lib').childFile('flutter_project_web.dart').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'web'
+    ], pluginClass: 'FlutterProjectWeb',
+    unexpectedPlatforms: <String>['some_platform'],
+    androidIdentifier: 'com.example.flutter_project',
+    webFileName: 'flutter_project_web.dart');
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
+  });
+
+  testUsingContext('plugin doe not support web if feature is not enabled', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=web', projectDir.path]);
+    expect(projectDir.childDirectory('lib').childFile('flutter_project_web.dart').existsSync(), false);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'some_platform'
+    ], pluginClass: 'somePluginClass',
+    unexpectedPlatforms: <String>['web']);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
+  });
+
+  testUsingContext('create an empty plugin, then add ios', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=ios', projectDir.path]);
+
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+  testUsingContext('create an empty plugin, then add android', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=android', projectDir.path]);
+
+    expect(projectDir.childDirectory('android').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('android').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+  testUsingContext('create an empty plugin, then add linux', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=linux', projectDir.path]);
+
+    expect(projectDir.childDirectory('linux').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('linux').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
+  });
+
+  testUsingContext('create an empty plugin, then add macos', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=macos', projectDir.path]);
+
+    expect(projectDir.childDirectory('macos').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('macos').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
+  });
+
+  testUsingContext('create an empty plugin, then add windows', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=windows', projectDir.path]);
+
+    expect(projectDir.childDirectory('windows').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('windows').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
+  });
+
+  testUsingContext('create an empty plugin, then add web', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=web', projectDir.path]);
+
+    expect(projectDir.childDirectory('lib').childFile('flutter_project_web.dart').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
+  });
+
+  testUsingContext('create a plugin with ios, then add macos', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=ios', projectDir.path]);
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'ios',
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform']);
+
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=macos', projectDir.path]);
+    expect(projectDir.childDirectory('macos').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('macos').existsSync(), true);
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
+  });
+
+  testUsingContext('create a plugin with ios and android', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>['create', '--no-pub', '--template=plugin', '--platforms=ios,android', projectDir.path]);
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+
+    expect(projectDir.childDirectory('android').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('android').existsSync(), true);
+    expect(projectDir.childDirectory('ios').existsSync(), true);
+    expect(projectDir.childDirectory('example').childDirectory('ios').existsSync(), true);
+    validatePubspecForPlugin(projectDir: projectDir.absolute.path, expectedPlatforms: const <String>[
+      'ios', 'android'
+    ], pluginClass: 'FlutterProjectPlugin',
+    unexpectedPlatforms: <String>['some_platform'],
+    androidIdentifier: 'com.example.flutter_project');
+  });
+
+  testUsingContext('create a module with --platforms throws error.', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await expectLater(
+      runner.run(<String>['create', '--no-pub', '--template=module', '--platforms=ios', projectDir.path])
+      , throwsToolExit(message: 'The "--platforms" argument is not supported', exitCode:2));
+  });
+
+  testUsingContext('create a package with --platforms throws error.', () async {
+    Cache.flutterRoot = '../..';
+    when(mockFlutterVersion.frameworkRevision).thenReturn(frameworkRevision);
+    when(mockFlutterVersion.channel).thenReturn(frameworkChannel);
+
+    final CreateCommand command = CreateCommand();
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await expectLater(
+      runner.run(<String>['create', '--no-pub', '--template=package', '--platforms=ios', projectDir.path])
+      , throwsToolExit(message: 'The "--platforms" argument is not supported', exitCode: 2));
+  });
+
 }
 
 Future<void> _createProject(
diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
index edd3eb9..fd4b612 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
@@ -375,7 +375,7 @@
     testUsingContext('get fetches packages and injects plugin in plugin project', () async {
       final String projectPath = await createProject(
         tempDir,
-        arguments: <String>['--template=plugin', '--no-pub'],
+        arguments: <String>['--template=plugin', '--no-pub', '--platforms=ios,android'],
       );
       final String exampleProjectPath = globals.fs.path.join(projectPath, 'example');
       removeGeneratedFiles(projectPath);
diff --git a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart
index 4d11c5e..f3207d5 100644
--- a/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart
+++ b/packages/flutter_tools/test/general.shard/flutter_manifest_test.dart
@@ -900,6 +900,49 @@
 
     expect(flutterManifest.isEmpty, false);
   });
+
+  testWithoutContext('FlutterManifest getSupportedPlatforms return null if runs on legacy format', () {
+    const String manifest = '''
+name: test
+flutter:
+  plugin:
+    androidPackage: com.example
+''';
+    final BufferLogger logger = BufferLogger.test();
+    final FlutterManifest flutterManifest = FlutterManifest.createFromString(
+      manifest,
+      logger: logger,
+    );
+
+    expect(flutterManifest.isPlugin, true);
+    expect(flutterManifest.supportedPlatforms, null);
+  });
+
+  testWithoutContext('FlutterManifest getSupportedPlatforms returns valid platforms.', () {
+    const String manifest = '''
+name: test
+flutter:
+  plugin:
+    platforms:
+      android:
+        package: com.example
+        pluginClass: SomeClass
+      ios:
+        pluginClass: SomeClass
+''';
+    final BufferLogger logger = BufferLogger.test();
+    final FlutterManifest flutterManifest = FlutterManifest.createFromString(
+      manifest,
+      logger: logger,
+    );
+
+    expect(flutterManifest.isPlugin, true);
+    expect(flutterManifest.supportedPlatforms['ios'],
+                              <String, dynamic>{'pluginClass': 'SomeClass'});
+    expect(flutterManifest.supportedPlatforms['android'],
+                              <String, dynamic>{'pluginClass': 'SomeClass',
+                                                'package': 'com.example'});
+  });
 }
 
 Matcher matchesManifest({
diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart
index cddb3cf..f3f4506 100644
--- a/packages/flutter_tools/test/general.shard/plugins_test.dart
+++ b/packages/flutter_tools/test/general.shard/plugins_test.dart
@@ -10,15 +10,18 @@
 import 'package:flutter_tools/src/base/time.dart';
 import 'package:flutter_tools/src/dart/package_map.dart';
 import 'package:flutter_tools/src/features.dart';
+import 'package:flutter_tools/src/globals.dart' as globals;
 import 'package:flutter_tools/src/ios/xcodeproj.dart';
 import 'package:flutter_tools/src/plugins.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/version.dart';
 import 'package:meta/meta.dart';
 import 'package:mockito/mockito.dart';
+import 'package:yaml/yaml.dart';
 
 import '../src/common.dart';
 import '../src/context.dart';
+import '../src/pubspec_schema.dart';
 
 void main() {
   group('plugins', () {
@@ -109,16 +112,14 @@
           ..writeAsStringSync('apackage:file://${dummyPackageDirectory.path}\n');
     });
 
-    // Makes the dummy package pointed to by packagesFile look like a plugin.
-    void configureDummyPackageAsPlugin() {
-      dummyPackageDirectory.parent.childFile('pubspec.yaml')..createSync(recursive: true)..writeAsStringSync('''
+    const String _pluginYaml = '''
   flutter:
     plugin:
       platforms:
         ios:
-          pluginClass: FLESomePlugin
+          pluginClass: SomePlugin
         macos:
-          pluginClass: FLESomePlugin
+          pluginClass: SomePlugin
         windows:
           pluginClass: SomePlugin
         linux:
@@ -129,7 +130,10 @@
         android:
           pluginClass: SomePlugin
           package: AndroidPackage
-  ''');
+  ''';
+    // Makes the dummy package pointed to by packagesFile look like a plugin.
+    void configureDummyPackageAsPlugin() {
+      dummyPackageDirectory.parent.childFile('pubspec.yaml')..createSync(recursive: true)..writeAsStringSync(_pluginYaml);
     }
 
     void createNewJavaPlugin1() {
@@ -1298,6 +1302,51 @@
         FeatureFlags: () => featureFlags,
       });
     });
+
+    group('pubspec', () {
+
+      Directory projectDir;
+      Directory tempDir;
+      setUp(() {
+        tempDir = globals.fs.systemTempDirectory.createTempSync('plugin_test.');
+        projectDir = tempDir.childDirectory('flutter_project');
+      });
+
+      tearDown(() {
+        tryToDelete(tempDir);
+      });
+
+      void _createPubspecFile(String yamlString) {
+        projectDir.childFile('pubspec.yaml')..createSync(recursive: true)..writeAsStringSync(yamlString);
+      }
+
+      test('validatePubspecForPlugin works', () async {
+        _createPubspecFile(_pluginYaml);
+        validatePubspecForPlugin(projectDir: projectDir.absolute.path, pluginClass: 'SomePlugin', expectedPlatforms: <String>[
+          'ios', 'macos', 'windows', 'linux', 'android', 'web'
+        ], androidIdentifier: 'AndroidPackage', webFileName: 'lib/SomeFile.dart');
+      });
+
+      test('createPlatformsYamlMap should create the correct map', () async {
+        final YamlMap map = Plugin.createPlatformsYamlMap(<String>['ios', 'android', 'linux'], 'PluginClass', 'some.android.package');
+        expect(map['ios'], <String, String> {
+          'pluginClass' : 'PluginClass'
+        });
+        expect(map['android'], <String, String> {
+          'pluginClass' : 'PluginClass',
+          'package': 'some.android.package',
+        });
+        expect(map['linux'], <String, String> {
+          'pluginClass' : 'PluginClass'
+        });
+      });
+
+      test('createPlatformsYamlMap should create empty map', () async {
+        final YamlMap map = Plugin.createPlatformsYamlMap(<String>[], null, null);
+        expect(map.isEmpty, true);
+      });
+
+    });
   });
 }
 
diff --git a/packages/flutter_tools/test/src/pubspec_schema.dart b/packages/flutter_tools/test/src/pubspec_schema.dart
index 730bd74..b9dd1ef 100644
--- a/packages/flutter_tools/test/src/pubspec_schema.dart
+++ b/packages/flutter_tools/test/src/pubspec_schema.dart
@@ -5,6 +5,10 @@
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/flutter_manifest.dart';
+import 'package:meta/meta.dart';
+import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:yaml/yaml.dart';
+import 'common.dart';
 
 /// Writes a schemaData used for validating pubspec.yaml files when parsing
 /// asset information.
@@ -22,3 +26,22 @@
 void writeEmptySchemaFile(FileSystem filesystem) {
   writeSchemaFile(filesystem, '{}');
 }
+
+/// Check if the pubspec.yaml file under the `projectDir` is valid for a plugin project.
+void validatePubspecForPlugin({@required String projectDir, @required String pluginClass, @required List<String> expectedPlatforms, List<String> unexpectedPlatforms = const <String>[], String androidIdentifier, String webFileName}) {
+    final FlutterManifest manifest = FlutterManifest.createFromPath(projectDir+'/pubspec.yaml', fileSystem: globals.fs, logger: globals.logger);
+    final YamlMap platformsMap = YamlMap.wrap(manifest.supportedPlatforms);
+    for (final String platform in expectedPlatforms) {
+      expect(platformsMap[platform], isNotNull);
+      expect(platformsMap[platform]['pluginClass'], pluginClass);
+      if (platform == 'android') {
+        expect(platformsMap[platform]['package'], androidIdentifier);
+      }
+      if (platform == 'web') {
+        expect(platformsMap[platform]['fileName'], webFileName);
+      }
+    }
+    for (final String platform in unexpectedPlatforms) {
+      expect(platformsMap[platform], isNull);
+    }
+}