Reland: use flutter features for web and desktop (#36699)


diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart
index e5a6659..9919e65 100644
--- a/packages/flutter_tools/lib/src/commands/assemble.dart
+++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -30,8 +30,6 @@
   @override
   String get name => 'assemble';
 
-  @override
-  bool get isExperimental => true;
 
   @override
   Future<FlutterCommandResult> runCommand() {
@@ -127,9 +125,6 @@
   String get name => 'run';
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<FlutterCommandResult> runCommand() async {
     final BuildResult result = await buildSystem.build(targetName, environment, BuildSystemConfig(
       resourcePoolSize: argResults['resource-pool-size'],
@@ -156,9 +151,6 @@
   String get name => 'describe';
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<FlutterCommandResult> runCommand() {
     try {
       printStatus(
@@ -181,9 +173,6 @@
   String get name => 'inputs';
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<FlutterCommandResult> runCommand() {
     try {
       final List<Map<String, Object>> results = buildSystem.describe(targetName, environment);
@@ -210,9 +199,6 @@
   String get name => 'build-dir';
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<FlutterCommandResult> runCommand() {
     printStatus(environment.buildDir.path);
     return null;
diff --git a/packages/flutter_tools/lib/src/commands/build_bundle.dart b/packages/flutter_tools/lib/src/commands/build_bundle.dart
index ef2235f..d15cfa4 100644
--- a/packages/flutter_tools/lib/src/commands/build_bundle.dart
+++ b/packages/flutter_tools/lib/src/commands/build_bundle.dart
@@ -8,10 +8,10 @@
 import '../base/file_system.dart';
 import '../build_info.dart';
 import '../bundle.dart';
+import '../features.dart';
 import '../project.dart';
 import '../reporting/usage.dart';
 import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult;
-import '../version.dart';
 import 'build.dart';
 
 class BuildBundleCommand extends BuildSubCommand {
@@ -98,13 +98,21 @@
     if (platform == null) {
       throwToolExit('Unknown platform: $targetPlatform');
     }
-    // Check for target platforms that are only allowed on unstable Flutter.
+    // Check for target platforms that are only allowed via feature flags.
     switch (platform) {
       case TargetPlatform.darwin_x64:
+        if (!featureFlags.isMacOSEnabled) {
+          throwToolExit('macOS is not a supported target platform.');
+        }
+        break;
       case TargetPlatform.windows_x64:
+        if (!featureFlags.isWindowsEnabled) {
+          throwToolExit('Windows is not a supported target platform.');
+        }
+        break;
       case TargetPlatform.linux_x64:
-        if (!FlutterVersion.instance.isMaster) {
-          throwToolExit('$targetPlatform is not supported on stable Flutter.');
+        if (!featureFlags.isLinuxEnabled) {
+          throwToolExit('Linux is not a supported target platform.');
         }
         break;
       default:
diff --git a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
index 473f943..39e0a84 100644
--- a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
+++ b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
@@ -25,9 +25,6 @@
   final String name = 'fuchsia';
 
   @override
-  bool isExperimental = true;
-
-  @override
   bool hidden = true;
 
   @override
diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart
index 4175b6d..e75e411 100644
--- a/packages/flutter_tools/lib/src/commands/build_linux.dart
+++ b/packages/flutter_tools/lib/src/commands/build_linux.dart
@@ -8,6 +8,7 @@
 import '../base/platform.dart';
 import '../build_info.dart';
 import '../cache.dart';
+import '../features.dart';
 import '../linux/build_linux.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart' show FlutterCommandResult;
@@ -35,9 +36,6 @@
   final String name = 'linux';
 
   @override
-  bool isExperimental = true;
-
-  @override
   bool hidden = true;
 
   @override
@@ -54,6 +52,9 @@
     Cache.releaseLockEarly();
     final BuildInfo buildInfo = getBuildInfo();
     final FlutterProject flutterProject = FlutterProject.current();
+    if (!featureFlags.isLinuxEnabled) {
+      throwToolExit('"build linux" is not currently supported.');
+    }
     if (!platform.isLinux) {
       throwToolExit('"build linux" only supported on Linux hosts.');
     }
diff --git a/packages/flutter_tools/lib/src/commands/build_macos.dart b/packages/flutter_tools/lib/src/commands/build_macos.dart
index e773f42..f871186 100644
--- a/packages/flutter_tools/lib/src/commands/build_macos.dart
+++ b/packages/flutter_tools/lib/src/commands/build_macos.dart
@@ -8,6 +8,7 @@
 import '../base/platform.dart';
 import '../build_info.dart';
 import '../cache.dart';
+import '../features.dart';
 import '../macos/build_macos.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart' show FlutterCommandResult;
@@ -35,9 +36,6 @@
   final String name = 'macos';
 
   @override
-  bool isExperimental = true;
-
-  @override
   bool hidden = true;
 
   @override
@@ -47,13 +45,16 @@
   };
 
   @override
-  String get description => 'build the macOS desktop target (Experimental).';
+  String get description => 'build the macOS desktop target.';
 
   @override
   Future<FlutterCommandResult> runCommand() async {
     Cache.releaseLockEarly();
     final BuildInfo buildInfo = getBuildInfo();
     final FlutterProject flutterProject = FlutterProject.current();
+    if (!featureFlags.isMacOSEnabled) {
+      throwToolExit('"build macos" is not currently supported.');
+    }
     if (!platform.isMacOS) {
       throwToolExit('"build macos" only supported on macOS hosts.');
     }
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
index 32cea06..575d1b0 100644
--- a/packages/flutter_tools/lib/src/commands/build_web.dart
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -4,7 +4,9 @@
 
 import 'dart:async';
 
+import '../base/common.dart';
 import '../build_info.dart';
+import '../features.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart'
     show DevelopmentArtifact, FlutterCommandResult;
@@ -32,13 +34,13 @@
   bool get hidden => true;
 
   @override
-  bool get isExperimental => true;
-
-  @override
-  final String description = '(EXPERIMENTAL) build a web application bundle.';
+  final String description = 'build a web application bundle.';
 
   @override
   Future<FlutterCommandResult> runCommand() async {
+    if (!featureFlags.isWebEnabled) {
+      throwToolExit('"build web" is not currently supported.');
+    }
     final FlutterProject flutterProject = FlutterProject.current();
     final String target = argResults['target'];
     final BuildInfo buildInfo = getBuildInfo();
diff --git a/packages/flutter_tools/lib/src/commands/build_windows.dart b/packages/flutter_tools/lib/src/commands/build_windows.dart
index 884d26f..e41de03 100644
--- a/packages/flutter_tools/lib/src/commands/build_windows.dart
+++ b/packages/flutter_tools/lib/src/commands/build_windows.dart
@@ -8,6 +8,7 @@
 import '../base/platform.dart';
 import '../build_info.dart';
 import '../cache.dart';
+import '../features.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart' show FlutterCommandResult;
 import '../windows/build_windows.dart';
@@ -35,9 +36,6 @@
   final String name = 'windows';
 
   @override
-  bool isExperimental = true;
-
-  @override
   bool hidden = true;
 
   @override
@@ -47,13 +45,16 @@
   };
 
   @override
-  String get description => 'build the desktop Windows target (Experimental).';
+  String get description => 'build the desktop Windows target.';
 
   @override
   Future<FlutterCommandResult> runCommand() async {
     Cache.releaseLockEarly();
     final FlutterProject flutterProject = FlutterProject.current();
     final BuildInfo buildInfo = getBuildInfo();
+    if (!featureFlags.isWindowsEnabled) {
+      throwToolExit('"build windows" is not currently supported.');
+    }
     if (!platform.isWindows) {
       throwToolExit('"build windows" only supported on Windows hosts.');
     }
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 17458ee..875beae 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -20,6 +20,7 @@
 import '../convert.dart';
 import '../dart/pub.dart';
 import '../doctor.dart';
+import '../features.dart';
 import '../globals.dart';
 import '../project.dart';
 import '../reporting/usage.dart';
@@ -613,7 +614,7 @@
       'iosLanguage': iosLanguage,
       'flutterRevision': FlutterVersion.instance.frameworkRevision,
       'flutterChannel': FlutterVersion.instance.channel,
-      'web': web && FlutterVersion.instance.isMaster
+      'web': web && featureFlags.isWebEnabled,
     };
   }
 
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 91de004..0254e3e 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -13,6 +13,7 @@
 import '../build_info.dart';
 import '../cache.dart';
 import '../device.dart';
+import '../features.dart';
 import '../globals.dart';
 import '../macos/xcode.dart';
 import '../project.dart';
@@ -410,11 +411,11 @@
       );
       flutterDevices.add(flutterDevice);
     }
-    // Only support "web mode" on non-stable branches with a single web device
-    // in a "hot mode".
-    final bool webMode = FlutterVersion.instance.isMaster
-      && devices.length == 1
-      && await devices.single.targetPlatform == TargetPlatform.web_javascript;
+    // Only support "web mode" with a single web device due to resident runner
+    // refactoring required otherwise.
+    final bool webMode = featureFlags.isWebEnabled &&
+                         devices.length == 1  &&
+                         await devices.single.targetPlatform == TargetPlatform.web_javascript;
 
     ResidentRunner runner;
     final String applicationBinaryPath = argResults['use-application-binary'];
diff --git a/packages/flutter_tools/lib/src/commands/unpack.dart b/packages/flutter_tools/lib/src/commands/unpack.dart
index 799815e..481c326 100644
--- a/packages/flutter_tools/lib/src/commands/unpack.dart
+++ b/packages/flutter_tools/lib/src/commands/unpack.dart
@@ -70,9 +70,6 @@
   bool get hidden => true;
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
     final Set<DevelopmentArtifact> result = <DevelopmentArtifact>{
       DevelopmentArtifact.universal,
diff --git a/packages/flutter_tools/lib/src/desktop.dart b/packages/flutter_tools/lib/src/desktop.dart
index 501e241..e62c4e7 100644
--- a/packages/flutter_tools/lib/src/desktop.dart
+++ b/packages/flutter_tools/lib/src/desktop.dart
@@ -4,32 +4,10 @@
 
 import 'dart:async';
 
-import 'package:meta/meta.dart';
-
-import 'base/common.dart';
 import 'base/io.dart';
-import 'base/platform.dart';
 import 'base/process_manager.dart';
 import 'convert.dart';
 import 'device.dart';
-import 'version.dart';
-
-@visibleForTesting
-bool debugDisableDesktop = false;
-
-/// Only launch or display desktop embedding devices from the command line
-/// or if `ENABLE_FLUTTER_DESKTOP` environment variable is set to true.
-bool get flutterDesktopEnabled {
-  if (debugDisableDesktop) {
-    return false;
-  }
-  if (isRunningFromDaemon) {
-    final bool platformEnabled = platform
-        .environment['ENABLE_FLUTTER_DESKTOP']?.toLowerCase() == 'true';
-    return platformEnabled && FlutterVersion.instance.isMaster;
-  }
-  return FlutterVersion.instance.isMaster;
-}
 
 /// Kills a process on linux or macOS.
 Future<bool> killProcess(String executable) async {
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 936bf15..734172f 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -14,9 +14,7 @@
 import 'base/file_system.dart';
 import 'base/utils.dart';
 import 'build_info.dart';
-import 'desktop.dart';
 import 'fuchsia/fuchsia_device.dart';
-
 import 'globals.dart';
 import 'ios/devices.dart';
 import 'ios/simulators.dart';
@@ -25,7 +23,6 @@
 import 'project.dart';
 import 'tester/flutter_tester.dart';
 import 'web/web_device.dart';
-import 'web/workflow.dart';
 import 'windows/windows_device.dart';
 
 DeviceManager get deviceManager => context.get<DeviceManager>();
@@ -74,23 +71,11 @@
     IOSSimulators(),
     FuchsiaDevices(),
     FlutterTesterDevices(),
-  ] + _conditionalDesktopDevices + _conditionalWebDevices);
-
-  /// Only add desktop devices if the flag is enabled.
-  static List<DeviceDiscovery> get _conditionalDesktopDevices {
-    return flutterDesktopEnabled ? <DeviceDiscovery>[
-      MacOSDevices(),
-      LinuxDevices(),
-      WindowsDevices(),
-    ] : <DeviceDiscovery>[];
-  }
-
-  /// Only add web devices if the flag is enabled.
-  static List<DeviceDiscovery> get _conditionalWebDevices {
-    return flutterWebEnabled ? <DeviceDiscovery>[
-      WebDevices(),
-    ] : <DeviceDiscovery>[];
-  }
+    MacOSDevices(),
+    LinuxDevices(),
+    WindowsDevices(),
+    WebDevices(),
+  ]);
 
   String _specifiedDeviceId;
 
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart
index fa4081d..cf697c7 100644
--- a/packages/flutter_tools/lib/src/doctor.dart
+++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -19,7 +19,6 @@
 import 'base/utils.dart';
 import 'base/version.dart';
 import 'cache.dart';
-import 'desktop.dart';
 import 'device.dart';
 import 'fuchsia/fuchsia_workflow.dart';
 import 'globals.dart';
@@ -74,12 +73,10 @@
           GroupedValidator(<DoctorValidator>[xcodeValidator, cocoapodsValidator]),
         if (webWorkflow.appliesToHostPlatform)
           const WebValidator(),
-        // Add desktop doctors to workflow if the flag is enabled.
-        if (flutterDesktopEnabled)
-          ...<DoctorValidator>[
-            if (linuxWorkflow.appliesToHostPlatform) LinuxDoctorValidator(),
-            if (windowsWorkflow.appliesToHostPlatform) visualStudioValidator,
-          ],
+        if (linuxWorkflow.appliesToHostPlatform)
+          LinuxDoctorValidator(),
+        if (windowsWorkflow.appliesToHostPlatform)
+          visualStudioValidator,
         if (ideValidators.isNotEmpty)
           ...ideValidators
         else
diff --git a/packages/flutter_tools/lib/src/linux/linux_workflow.dart b/packages/flutter_tools/lib/src/linux/linux_workflow.dart
index 4959f72..e51c26b 100644
--- a/packages/flutter_tools/lib/src/linux/linux_workflow.dart
+++ b/packages/flutter_tools/lib/src/linux/linux_workflow.dart
@@ -4,8 +4,8 @@
 
 import '../base/context.dart';
 import '../base/platform.dart';
-import '../desktop.dart';
 import '../doctor.dart';
+import '../features.dart';
 
 /// The [WindowsWorkflow] instance.
 LinuxWorkflow get linuxWorkflow => context.get<LinuxWorkflow>();
@@ -18,13 +18,13 @@
   const LinuxWorkflow();
 
   @override
-  bool get appliesToHostPlatform => platform.isLinux;
+  bool get appliesToHostPlatform => platform.isLinux && featureFlags.isLinuxEnabled;
 
   @override
-  bool get canLaunchDevices => platform.isLinux && flutterDesktopEnabled;
+  bool get canLaunchDevices => platform.isLinux && featureFlags.isLinuxEnabled;
 
   @override
-  bool get canListDevices => platform.isLinux && flutterDesktopEnabled;
+  bool get canListDevices => platform.isLinux && featureFlags.isLinuxEnabled;
 
   @override
   bool get canListEmulators => false;
diff --git a/packages/flutter_tools/lib/src/macos/macos_workflow.dart b/packages/flutter_tools/lib/src/macos/macos_workflow.dart
index e85e3d4..1c0391f 100644
--- a/packages/flutter_tools/lib/src/macos/macos_workflow.dart
+++ b/packages/flutter_tools/lib/src/macos/macos_workflow.dart
@@ -4,8 +4,8 @@
 
 import '../base/context.dart';
 import '../base/platform.dart';
-import '../desktop.dart';
 import '../doctor.dart';
+import '../features.dart';
 
 /// The [MacOSWorkflow] instance.
 MacOSWorkflow get macOSWorkflow => context.get<MacOSWorkflow>();
@@ -18,13 +18,13 @@
   const MacOSWorkflow();
 
   @override
-  bool get appliesToHostPlatform => platform.isMacOS;
+  bool get appliesToHostPlatform => platform.isMacOS && featureFlags.isMacOSEnabled;
 
   @override
-  bool get canLaunchDevices => platform.isMacOS && flutterDesktopEnabled;
+  bool get canLaunchDevices => platform.isMacOS && featureFlags.isMacOSEnabled;
 
   @override
-  bool get canListDevices => platform.isMacOS && flutterDesktopEnabled;
+  bool get canListDevices => platform.isMacOS && featureFlags.isMacOSEnabled;
 
   @override
   bool get canListEmulators => false;
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index 0242a62..021ec84 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -9,7 +9,7 @@
 
 import 'base/file_system.dart';
 import 'dart/package_map.dart';
-import 'desktop.dart';
+import 'features.dart';
 import 'globals.dart';
 import 'macos/cocoapods.dart';
 import 'project.dart';
@@ -364,7 +364,7 @@
   // TODO(stuartmorgan): Revisit the condition here once the plans for handling
   // desktop in existing projects are in place. For now, ignore checkProjects
   // on desktop and always treat it as true.
-  if (flutterDesktopEnabled && project.macos.existsSync()) {
+  if (featureFlags.isMacOSEnabled && project.macos.existsSync()) {
     await _writeMacOSPluginRegistrant(project, plugins);
   }
   for (final XcodeBasedProject subproject in <XcodeBasedProject>[project.ios, project.macos]) {
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index 0a1b5b8..95b526e 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -14,7 +14,7 @@
 import 'build_info.dart';
 import 'bundle.dart' as bundle;
 import 'cache.dart';
-import 'desktop.dart';
+import 'features.dart';
 import 'flutter_manifest.dart';
 import 'globals.dart';
 import 'ios/ios_workflow.dart';
@@ -22,7 +22,6 @@
 import 'ios/xcodeproj.dart' as xcode;
 import 'plugins.dart';
 import 'template.dart';
-import 'web/workflow.dart';
 
 FlutterProjectFactory get projectFactory => context.get<FlutterProjectFactory>() ?? const FlutterProjectFactory();
 
@@ -206,10 +205,10 @@
     }
     // TODO(stuartmorgan): Add checkProjects logic once a create workflow exists
     // for macOS. For now, always treat checkProjects as true for macOS.
-    if (flutterDesktopEnabled && macos.existsSync()) {
+    if (featureFlags.isMacOSEnabled && macos.existsSync()) {
       await macos.ensureReadyForPlatformSpecificTooling();
     }
-    if (flutterWebEnabled && web.existsSync()) {
+    if (featureFlags.isWebEnabled && web.existsSync()) {
       await web.ensureReadyForPlatformSpecificTooling();
     }
     await injectPlugins(this, checkProjects: checkProjects);
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index e9d9211..2f56364 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -25,10 +25,10 @@
 import '../dart/pub.dart';
 import '../device.dart';
 import '../doctor.dart';
+import '../features.dart';
 import '../globals.dart';
 import '../project.dart';
 import '../reporting/usage.dart';
-import '../version.dart';
 import 'flutter_command_runner.dart';
 
 export '../cache.dart' show DevelopmentArtifact;
@@ -355,11 +355,6 @@
     }
   }
 
-  /// Whether this feature should not be usable on stable branches.
-  ///
-  /// Defaults to false, meaning it is usable.
-  bool get isExperimental => false;
-
   /// Additional usage values to be sent with the usage ping.
   Future<Map<String, String>> get usageValues async => const <String, String>{};
 
@@ -555,12 +550,6 @@
   @protected
   @mustCallSuper
   Future<void> validateCommand() async {
-    // If we're on a stable branch, then don't allow the usage of
-    // "experimental" features.
-    if (isExperimental && !FlutterVersion.instance.isMaster) {
-      throwToolExit('Experimental feature $name is not supported on stable branches');
-    }
-
     if (_requiresPubspecYaml && !PackageMap.isUsingCustomPackagesPath) {
       // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path.
       if (!fs.isFileSync('pubspec.yaml')) {
@@ -653,17 +642,17 @@
     case TargetPlatform.ios:
       return DevelopmentArtifact.iOS;
     case TargetPlatform.darwin_x64:
-      if (FlutterVersion.instance.isMaster) {
+      if (featureFlags.isMacOSEnabled) {
         return DevelopmentArtifact.macOS;
       }
       return null;
     case TargetPlatform.windows_x64:
-      if (!FlutterVersion.instance.isMaster) {
+      if (featureFlags.isWindowsEnabled) {
         return DevelopmentArtifact.windows;
       }
       return null;
     case TargetPlatform.linux_x64:
-      if (!FlutterVersion.instance.isMaster) {
+      if (featureFlags.isLinuxEnabled) {
         return DevelopmentArtifact.linux;
       }
       return null;
diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart
index ecd8f41..689eb72 100644
--- a/packages/flutter_tools/lib/src/web/web_device.dart
+++ b/packages/flutter_tools/lib/src/web/web_device.dart
@@ -11,6 +11,7 @@
 import '../base/process_manager.dart';
 import '../build_info.dart';
 import '../device.dart';
+import '../features.dart';
 import '../project.dart';
 import 'chrome.dart';
 import 'workflow.dart';
@@ -74,7 +75,7 @@
   Future<String> get emulatorId async => null;
 
   @override
-  bool isSupported() => flutterWebEnabled && canFindChrome();
+  bool isSupported() =>  featureFlags.isWebEnabled && canFindChrome();
 
   @override
   String get name => 'Chrome';
@@ -151,7 +152,7 @@
   final ChromeDevice _webDevice = ChromeDevice();
 
   @override
-  bool get canListAnything => flutterWebEnabled;
+  bool get canListAnything => featureFlags.isWebEnabled;
 
   @override
   Future<List<Device>> pollingGetDevices() async {
@@ -161,7 +162,7 @@
   }
 
   @override
-  bool get supportsPlatform => flutterWebEnabled;
+  bool get supportsPlatform =>  featureFlags.isWebEnabled;
 }
 
 @visibleForTesting
diff --git a/packages/flutter_tools/lib/src/web/workflow.dart b/packages/flutter_tools/lib/src/web/workflow.dart
index 925ec3d..1f388ca 100644
--- a/packages/flutter_tools/lib/src/web/workflow.dart
+++ b/packages/flutter_tools/lib/src/web/workflow.dart
@@ -2,33 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:meta/meta.dart';
-
-import '../base/common.dart';
 import '../base/context.dart';
 import '../base/platform.dart';
 import '../base/process_manager.dart';
 import '../doctor.dart';
-import '../version.dart';
+import '../features.dart';
 import 'chrome.dart';
 
-@visibleForTesting
-bool debugDisableWeb = false;
-
-/// Only launch or display web devices if `FLUTTER_WEB`
-/// environment variable is set to true from the daemon.
-bool get flutterWebEnabled {
-  if (debugDisableWeb) {
-    return false;
-  }
-  if (isRunningFromDaemon) {
-    final bool platformEnabled = platform
-        .environment['FLUTTER_WEB']?.toLowerCase() == 'true';
-    return platformEnabled && FlutterVersion.instance.isMaster;
-  }
-  return FlutterVersion.instance.isMaster;
-}
-
 /// The  web workflow instance.
 WebWorkflow get webWorkflow => context.get<WebWorkflow>();
 
@@ -36,13 +16,13 @@
   const WebWorkflow();
 
   @override
-  bool get appliesToHostPlatform => flutterWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux);
+  bool get appliesToHostPlatform => featureFlags.isWebEnabled && (platform.isWindows || platform.isMacOS || platform.isLinux);
 
   @override
-  bool get canLaunchDevices => flutterWebEnabled && canFindChrome();
+  bool get canLaunchDevices => featureFlags.isWebEnabled && canFindChrome();
 
   @override
-  bool get canListDevices => flutterWebEnabled && canFindChrome();
+  bool get canListDevices => featureFlags.isWebEnabled && canFindChrome();
 
   @override
   bool get canListEmulators => false;
diff --git a/packages/flutter_tools/lib/src/windows/windows_workflow.dart b/packages/flutter_tools/lib/src/windows/windows_workflow.dart
index 8d8e34e..2a423bb 100644
--- a/packages/flutter_tools/lib/src/windows/windows_workflow.dart
+++ b/packages/flutter_tools/lib/src/windows/windows_workflow.dart
@@ -4,8 +4,8 @@
 
 import '../base/context.dart';
 import '../base/platform.dart';
-import '../desktop.dart';
 import '../doctor.dart';
+import '../features.dart';
 
 /// The [WindowsWorkflow] instance.
 WindowsWorkflow get windowsWorkflow => context.get<WindowsWorkflow>();
@@ -18,13 +18,13 @@
   const WindowsWorkflow();
 
   @override
-  bool get appliesToHostPlatform => platform.isWindows;
+  bool get appliesToHostPlatform => platform.isWindows && featureFlags.isWindowsEnabled;
 
   @override
-  bool get canLaunchDevices => platform.isWindows && flutterDesktopEnabled;
+  bool get canLaunchDevices => platform.isWindows && featureFlags.isWindowsEnabled;
 
   @override
-  bool get canListDevices => platform.isWindows && flutterDesktopEnabled;
+  bool get canListDevices => platform.isWindows && featureFlags.isWindowsEnabled;
 
   @override
   bool get canListEmulators => false;
diff --git a/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart
index 23e7a41..782ac26 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_bundle_test.dart
@@ -3,93 +3,196 @@
 // found in the LICENSE file.
 
 import 'package:args/command_runner.dart';
+import 'package:file/memory.dart';
+import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/build_bundle.dart';
 import 'package:flutter_tools/src/bundle.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/reporting/usage.dart';
 import 'package:mockito/mockito.dart';
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/testbed.dart';
 
 void main() {
   Cache.disableLocking();
+  Directory tempDir;
+  MockBundleBuilder mockBundleBuilder;
 
-  group('getUsage', () {
-    Directory tempDir;
-    MockBundleBuilder mockBundleBuilder;
+  setUp(() {
+    tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
 
-    setUp(() {
-      tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
+    mockBundleBuilder = MockBundleBuilder();
+    when(
+      mockBundleBuilder.build(
+        platform: anyNamed('platform'),
+        buildMode: anyNamed('buildMode'),
+        mainPath: anyNamed('mainPath'),
+        manifestPath: anyNamed('manifestPath'),
+        applicationKernelFilePath: anyNamed('applicationKernelFilePath'),
+        depfilePath: anyNamed('depfilePath'),
+        privateKeyPath: anyNamed('privateKeyPath'),
+        assetDirPath: anyNamed('assetDirPath'),
+        packagesPath: anyNamed('packagesPath'),
+        precompiledSnapshot: anyNamed('precompiledSnapshot'),
+        reportLicensedPackages: anyNamed('reportLicensedPackages'),
+        trackWidgetCreation: anyNamed('trackWidgetCreation'),
+        extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
+        extraGenSnapshotOptions: anyNamed('extraGenSnapshotOptions'),
+        fileSystemRoots: anyNamed('fileSystemRoots'),
+        fileSystemScheme: anyNamed('fileSystemScheme'),
+      ),
+    ).thenAnswer((_) => Future<void>.value());
+  });
 
-      mockBundleBuilder = MockBundleBuilder();
-      when(
-        mockBundleBuilder.build(
-          platform: anyNamed('platform'),
-          buildMode: anyNamed('buildMode'),
-          mainPath: anyNamed('mainPath'),
-          manifestPath: anyNamed('manifestPath'),
-          applicationKernelFilePath: anyNamed('applicationKernelFilePath'),
-          depfilePath: anyNamed('depfilePath'),
-          privateKeyPath: anyNamed('privateKeyPath'),
-          assetDirPath: anyNamed('assetDirPath'),
-          packagesPath: anyNamed('packagesPath'),
-          precompiledSnapshot: anyNamed('precompiledSnapshot'),
-          reportLicensedPackages: anyNamed('reportLicensedPackages'),
-          trackWidgetCreation: anyNamed('trackWidgetCreation'),
-          extraFrontEndOptions: anyNamed('extraFrontEndOptions'),
-          extraGenSnapshotOptions: anyNamed('extraGenSnapshotOptions'),
-          fileSystemRoots: anyNamed('fileSystemRoots'),
-          fileSystemScheme: anyNamed('fileSystemScheme'),
-        ),
-      ).thenAnswer((_) => Future<void>.value());
-    });
+  tearDown(() {
+    tryToDelete(tempDir);
+  });
 
-    tearDown(() {
-      tryToDelete(tempDir);
-    });
+  Future<BuildBundleCommand> runCommandIn(String projectPath, { List<String> arguments }) async {
+    final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: mockBundleBuilder);
+    final CommandRunner<void> runner = createTestCommandRunner(command);
+    await runner.run(<String>[
+      'bundle',
+      ...?arguments,
+      '--target=$projectPath/lib/main.dart',
+    ]);
+    return command;
+  }
 
-    Future<BuildBundleCommand> runCommandIn(String projectPath, { List<String> arguments }) async {
-      final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: mockBundleBuilder);
-      final CommandRunner<void> runner = createTestCommandRunner(command);
-      await runner.run(<String>[
-        'bundle',
-        ...?arguments,
-        '--target=$projectPath/lib/main.dart',
-      ]);
-      return command;
-    }
+  testUsingContext('bundle getUsage indicate that project is a module', () async {
+    final String projectPath = await createProject(tempDir,
+        arguments: <String>['--no-pub', '--template=module']);
 
-    testUsingContext('indicate that project is a module', () async {
-      final String projectPath = await createProject(tempDir,
-          arguments: <String>['--no-pub', '--template=module']);
+    final BuildBundleCommand command = await runCommandIn(projectPath);
 
-      final BuildBundleCommand command = await runCommandIn(projectPath);
+    expect(await command.usageValues,
+        containsPair(kCommandBuildBundleIsModule, 'true'));
+  }, timeout: allowForCreateFlutterProject);
 
-      expect(await command.usageValues,
-          containsPair(kCommandBuildBundleIsModule, 'true'));
-    }, timeout: allowForCreateFlutterProject);
+  testUsingContext('bundle getUsage indicate that project is not a module', () async {
+    final String projectPath = await createProject(tempDir,
+        arguments: <String>['--no-pub', '--template=app']);
 
-    testUsingContext('indicate that project is not a module', () async {
-      final String projectPath = await createProject(tempDir,
-          arguments: <String>['--no-pub', '--template=app']);
+    final BuildBundleCommand command = await runCommandIn(projectPath);
 
-      final BuildBundleCommand command = await runCommandIn(projectPath);
+    expect(await command.usageValues,
+        containsPair(kCommandBuildBundleIsModule, 'false'));
+  }, timeout: allowForCreateFlutterProject);
 
-      expect(await command.usageValues,
-          containsPair(kCommandBuildBundleIsModule, 'false'));
-    }, timeout: allowForCreateFlutterProject);
+  testUsingContext('bundle getUsage indicate the target platform', () async {
+    final String projectPath = await createProject(tempDir,
+        arguments: <String>['--no-pub', '--template=app']);
 
-    testUsingContext('indicate the target platform', () async {
-      final String projectPath = await createProject(tempDir,
-          arguments: <String>['--no-pub', '--template=app']);
+    final BuildBundleCommand command = await runCommandIn(projectPath);
 
-      final BuildBundleCommand command = await runCommandIn(projectPath);
+    expect(await command.usageValues,
+        containsPair(kCommandBuildBundleTargetPlatform, 'android-arm'));
+  }, timeout: allowForCreateFlutterProject);
 
-      expect(await command.usageValues,
-          containsPair(kCommandBuildBundleTargetPlatform, 'android-arm'));
-    }, timeout: allowForCreateFlutterProject);
+  testUsingContext('bundle fails to build for Windows if feature is disabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync(recursive: true);
+    fs.file('.packages').createSync(recursive: true);
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    expect(() => runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=windows-x64'
+    ]), throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
+  });
+
+  testUsingContext('bundle fails to build for Linux if feature is disabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync();
+    fs.file('.packages').createSync();
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    expect(() => runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=linux-x64'
+    ]), throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
+
+  testUsingContext('bundle fails to build for macOS if feature is disabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync();
+    fs.file('.packages').createSync();
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    expect(() => runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=darwin-x64'
+    ]), throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
+  });
+
+  testUsingContext('bundle can build for Windows if feature is enabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync();
+    fs.file('.packages').createSync();
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    await runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=windows-x64'
+    ]);
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
+  });
+
+  testUsingContext('bundle can build for Linux if feature is enabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync();
+    fs.file('.packages').createSync();
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    await runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=linux-x64'
+    ]);
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
+  });
+
+  testUsingContext('bundle can build for macOS if feature is enabled', () async {
+    fs.file('lib/main.dart').createSync(recursive: true);
+    fs.file('pubspec.yaml').createSync();
+    fs.file('.packages').createSync();
+    final CommandRunner<void> runner = createTestCommandRunner(BuildBundleCommand()
+        ..bundleBuilder = MockBundleBuilder());
+
+    await runner.run(<String>[
+      'bundle',
+      '--no-pub',
+      '--target-platform=darwin-x64'
+    ]);
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
index 638a20b..0e1c809 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:args/command_runner.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -9,6 +10,7 @@
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/build.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/linux/makefile.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:mockito/mockito.dart';
@@ -17,6 +19,7 @@
 import '../../src/common.dart';
 import '../../src/context.dart';
 import '../../src/mocks.dart';
+import '../../src/testbed.dart';
 
 void main() {
   MockProcessManager mockProcessManager;
@@ -55,6 +58,7 @@
   }, overrides: <Type, Generator>{
     Platform: () => linuxPlatform,
     FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
   });
 
   testUsingContext('Linux build fails on non-linux platform', () async {
@@ -71,6 +75,7 @@
   }, overrides: <Type, Generator>{
     Platform: () => notLinuxPlatform,
     FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
   });
 
   testUsingContext('Linux build invokes make and writes temporary files', () async {
@@ -97,6 +102,7 @@
     FileSystem: () => MemoryFileSystem(),
     ProcessManager: () => mockProcessManager,
     Platform: () => linuxPlatform,
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
   });
 
   testUsingContext('linux can extract binary name from Makefile', () async {
@@ -112,7 +118,19 @@
     final FlutterProject flutterProject = FlutterProject.current();
 
     expect(makefileExecutableName(flutterProject.linux), 'fizz_bar');
-  }, overrides: <Type, Generator>{FileSystem: () => MemoryFileSystem()});
+  }, overrides: <Type, Generator>{
+    FileSystem: () => MemoryFileSystem(),
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
+  });
+
+  testUsingContext('Refuses to build for Linux when feature is disabled', () {
+    final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
+
+    expect(() => runner.run(<String>['build', 'linux']),
+        throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  });
 }
 
 class MockProcessManager extends Mock implements ProcessManager {}
diff --git a/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart b/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart
index ceb07b8..462e2d8 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_macos_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:args/command_runner.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -10,6 +11,7 @@
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/build.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
@@ -17,6 +19,7 @@
 import '../../src/common.dart';
 import '../../src/context.dart';
 import '../../src/mocks.dart';
+import '../../src/testbed.dart';
 
 void main() {
   MockProcessManager mockProcessManager;
@@ -56,6 +59,7 @@
     ), throwsA(isInstanceOf<ToolExit>()));
   }, overrides: <Type, Generator>{
     Platform: () => macosPlatform,
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
   });
 
   testUsingContext('macOS build fails on non-macOS platform', () async {
@@ -71,6 +75,7 @@
   }, overrides: <Type, Generator>{
     Platform: () => notMacosPlatform,
     FileSystem: () => memoryFilesystem,
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
   });
 
   testUsingContext('macOS build invokes build script', () async {
@@ -104,6 +109,16 @@
     FileSystem: () => memoryFilesystem,
     ProcessManager: () => mockProcessManager,
     Platform: () => macosPlatform,
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
+  });
+
+  testUsingContext('Refuses to build for macOS when feature is disabled', () {
+    final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
+
+    expect(() => runner.run(<String>['build', 'macos']),
+        throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
index 2adb818..3046570 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
@@ -2,12 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:args/command_runner.dart';
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/build_info.dart';
 import 'package:flutter_tools/src/cache.dart';
+import 'package:flutter_tools/src/commands/build.dart';
 import 'package:flutter_tools/src/device.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/resident_runner.dart';
 import 'package:flutter_tools/src/resident_web_runner.dart';
@@ -24,6 +27,7 @@
   MockPlatform mockPlatform;
 
   setUpAll(() {
+    Cache.flutterRoot = '';
     Cache.disableLocking();
   });
 
@@ -49,6 +53,7 @@
       WebCompilationProxy: () => mockWebCompilationProxy,
       Platform: () => mockPlatform,
       FlutterVersion: () => MockFlutterVersion(),
+      FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
     });
   });
 
@@ -82,6 +87,15 @@
       BuildInfo.debug,
     );
   }));
+
+  test('Refuses to build for web when feature is disabled', () => testbed.run(() async {
+    final CommandRunner<void> runner = createTestCommandRunner(BuildCommand());
+
+    expect(() => runner.run(<String>['build', 'web']),
+        throwsA(isInstanceOf<ToolExit>()));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
+  }));
 }
 
 class MockWebCompilationProxy extends Mock implements WebCompilationProxy {}
diff --git a/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart b/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart
index 301fa7d..948eae0 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_windows_test.dart
@@ -9,6 +9,7 @@
 import 'package:flutter_tools/src/base/platform.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/build.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/windows/visual_studio.dart';
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
@@ -17,6 +18,7 @@
 import '../../src/common.dart';
 import '../../src/context.dart';
 import '../../src/mocks.dart';
+import '../../src/testbed.dart';
 
 void main() {
   MockProcessManager mockProcessManager;
@@ -65,6 +67,7 @@
     Platform: () => windowsPlatform,
     FileSystem: () => memoryFilesystem,
     VisualStudio: () => mockVisualStudio,
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
   });
 
   testUsingContext('Windows build fails when there is no windows project', () async {
@@ -78,6 +81,7 @@
     Platform: () => windowsPlatform,
     FileSystem: () => memoryFilesystem,
     VisualStudio: () => mockVisualStudio,
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
   });
 
   testUsingContext('Windows build fails on non windows platform', () async {
@@ -96,6 +100,7 @@
     Platform: () => notWindowsPlatform,
     FileSystem: () => memoryFilesystem,
     VisualStudio: () => mockVisualStudio,
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
   });
 
   testUsingContext('Windows build invokes msbuild and writes generated files', () async {
@@ -132,6 +137,7 @@
     ProcessManager: () => mockProcessManager,
     Platform: () => windowsPlatform,
     VisualStudio: () => mockVisualStudio,
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/commands/config_test.dart b/packages/flutter_tools/test/general.shard/commands/config_test.dart
index e7c87e8..16fec40 100644
--- a/packages/flutter_tools/test/general.shard/commands/config_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/config_test.dart
@@ -12,9 +12,7 @@
 import 'package:flutter_tools/src/base/logger.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/config.dart';
-import 'package:flutter_tools/src/desktop.dart';
 import 'package:flutter_tools/src/version.dart';
-import 'package:flutter_tools/src/web/workflow.dart';
 import 'package:mockito/mockito.dart';
 
 import '../../src/common.dart';
@@ -26,9 +24,6 @@
   MockFlutterVersion mockFlutterVersion;
 
   setUpAll(() {
-    // TODO(jonahwilliams): remove once features are landed.
-    debugDisableDesktop = true;
-    debugDisableWeb = true;
     Cache.disableLocking();
   });
 
diff --git a/packages/flutter_tools/test/general.shard/commands/devices_test.dart b/packages/flutter_tools/test/general.shard/commands/devices_test.dart
index 01bbb9a..ee6e095 100644
--- a/packages/flutter_tools/test/general.shard/commands/devices_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/devices_test.dart
@@ -20,9 +20,6 @@
   group('devices', () {
     setUpAll(() {
       Cache.disableLocking();
-      // TODO(jonahwilliams): adjust the individual tests so they do not
-      // depend on the host environment.
-      debugDisableWebAndDesktop = true;
     });
 
     testUsingContext('returns 0 when called', () async {
diff --git a/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart b/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart
deleted file mode 100644
index 6097ba0..0000000
--- a/packages/flutter_tools/test/general.shard/commands/update_packages_test.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2015 The Chromium 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:flutter_tools/src/commands/update_packages.dart';
-
-import '../../src/common.dart';
-import '../../src/context.dart';
-
-void main() {
-  group('UpdatePackagesCommand', () {
-    // Marking it as experimental breaks bots tests and packaging scripts on stable branches.
-    testUsingContext('is not marked as experimental', () async {
-      final UpdatePackagesCommand command = UpdatePackagesCommand();
-      expect(command.isExperimental, isFalse);
-    });
-  });
-}
diff --git a/packages/flutter_tools/test/general.shard/doctor.dart b/packages/flutter_tools/test/general.shard/doctor.dart
new file mode 100644
index 0000000..db574e6
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/doctor.dart
@@ -0,0 +1,62 @@
+// Copyright 2019 The Chromium 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:flutter_tools/src/doctor.dart';
+import 'package:flutter_tools/src/features.dart';
+import 'package:flutter_tools/src/linux/linux_doctor.dart';
+import 'package:flutter_tools/src/web/web_validator.dart';
+import 'package:flutter_tools/src/windows/visual_studio_validator.dart';
+
+import '../src/common.dart';
+import '../src/testbed.dart';
+
+void main() {
+  Testbed testbed;
+
+  setUp(() {
+    testbed = Testbed();
+  });
+
+  test('doctor validators includes desktop when features are enabled', () => testbed.run(() {
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        contains(isInstanceOf<LinuxDoctorValidator>()));
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        contains(isInstanceOf<VisualStudioValidator>()));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(
+      isLinuxEnabled: true,
+      isWindowsEnabled: true,
+    )
+  }));
+
+  test('doctor validators does not include desktop when features are enabled', () => testbed.run(() {
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        isNot(contains(isInstanceOf<LinuxDoctorValidator>())));
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        isNot(contains(isInstanceOf<VisualStudioValidator>())));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(
+      isLinuxEnabled: false,
+      isWindowsEnabled: false,
+    )
+  }));
+
+  test('doctor validators includes web when feature is enabled', () => testbed.run(() {
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        contains(isInstanceOf<WebValidator>()));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(
+      isWebEnabled: true,
+    )
+  }));
+
+  test('doctor validators does not include web when feature is disabled', () => testbed.run(() {
+    expect(DoctorValidatorsProvider.defaultInstance.validators,
+        isNot(contains(isInstanceOf<WebValidator>())));
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(
+      isWebEnabled: false,
+    )
+  }));
+}
diff --git a/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart
index 1445cff..8723c47 100644
--- a/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/linux/linux_workflow_test.dart
@@ -2,42 +2,57 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter_tools/src/features.dart';
 import 'package:mockito/mockito.dart';
 import 'package:flutter_tools/src/linux/linux_workflow.dart';
 import 'package:flutter_tools/src/base/platform.dart';
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/testbed.dart';
 
 void main() {
-  group(LinuxWorkflow, () {
-    final MockPlatform linux = MockPlatform();
-    final MockPlatform linuxWithFde = MockPlatform()
-      ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
-    final MockPlatform notLinux = MockPlatform();
+  MockPlatform linux;
+  MockPlatform notLinux;
+  Testbed testbed;
+
+  setUp(() {
+    linux = MockPlatform();
+    notLinux = MockPlatform();
     when(linux.isLinux).thenReturn(true);
-    when(linuxWithFde.isLinux).thenReturn(true);
     when(notLinux.isLinux).thenReturn(false);
-
-    testUsingContext('Applies to linux platform', () {
-      expect(linuxWorkflow.appliesToHostPlatform, true);
-    }, overrides: <Type, Generator>{
-      Platform: () => linux,
-    });
-    testUsingContext('Does not apply to non-linux platform', () {
-      expect(linuxWorkflow.appliesToHostPlatform, false);
-    }, overrides: <Type, Generator>{
-      Platform: () => notLinux,
-    });
-
-    testUsingContext('defaults', () {
-      expect(linuxWorkflow.canListEmulators, false);
-      expect(linuxWorkflow.canLaunchDevices, true);
-      expect(linuxWorkflow.canListDevices, true);
-    }, overrides: <Type, Generator>{
-      Platform: () => linuxWithFde,
-    });
+    testbed = Testbed(
+      overrides: <Type, Generator>{
+        Platform: () => linux,
+        FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: true),
+      }
+    );
   });
+
+  test('Applies to linux platform', () => testbed.run(() {
+    expect(linuxWorkflow.appliesToHostPlatform, true);
+    expect(linuxWorkflow.canLaunchDevices, true);
+    expect(linuxWorkflow.canListDevices, true);
+    expect(linuxWorkflow.canListEmulators, false);
+  }));
+
+  test('Does not apply to non-linux platform', () => testbed.run(() {
+    expect(linuxWorkflow.appliesToHostPlatform, false);
+    expect(linuxWorkflow.canLaunchDevices, false);
+    expect(linuxWorkflow.canListDevices, false);
+    expect(linuxWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    Platform: () => notLinux,
+  }));
+
+  test('Does not apply when feature is disabled', () => testbed.run(() {
+    expect(linuxWorkflow.appliesToHostPlatform, false);
+    expect(linuxWorkflow.canLaunchDevices, false);
+    expect(linuxWorkflow.canListDevices, false);
+    expect(linuxWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isLinuxEnabled: false),
+  }));
 }
 
 class MockPlatform extends Mock implements Platform {
diff --git a/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart b/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart
index b63bdde..9dadfe9 100644
--- a/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/macos_workflow_test.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:mockito/mockito.dart';
 
 import 'package:flutter_tools/src/base/platform.dart';
@@ -11,40 +11,48 @@
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/testbed.dart';
 
 void main() {
-  group(MacOSWorkflow, () {
-    final MockPlatform mac = MockPlatform();
-    final MockPlatform macWithFde = MockPlatform()
-      ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
-    final MockPlatform notMac = MockPlatform();
+  MockPlatform mac;
+  MockPlatform notMac;
+  Testbed testbed;
+
+  setUp(() {
+    mac = MockPlatform();
+    notMac = MockPlatform();
     when(mac.isMacOS).thenReturn(true);
-    when(macWithFde.isMacOS).thenReturn(true);
     when(notMac.isMacOS).thenReturn(false);
-
-    final MockProcessManager mockProcessManager = MockProcessManager();
-    when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
-      return ProcessResult(0, 1, '', '');
-    });
-    testUsingContext('Applies to mac platform', () {
-      expect(macOSWorkflow.appliesToHostPlatform, true);
-    }, overrides: <Type, Generator>{
+    testbed = Testbed(overrides: <Type, Generator>{
       Platform: () => mac,
-    });
-    testUsingContext('Does not apply to non-mac platform', () {
-      expect(macOSWorkflow.appliesToHostPlatform, false);
-    }, overrides: <Type, Generator>{
-      Platform: () => notMac,
-    });
-
-    testUsingContext('defaults', () {
-      expect(macOSWorkflow.canListEmulators, false);
-      expect(macOSWorkflow.canLaunchDevices, true);
-      expect(macOSWorkflow.canListDevices, true);
-    }, overrides: <Type, Generator>{
-      Platform: () => macWithFde,
+      FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
     });
   });
+
+  test('Applies to macOS platform', () => testbed.run(() {
+    expect(macOSWorkflow.appliesToHostPlatform, true);
+    expect(macOSWorkflow.canListDevices, true);
+    expect(macOSWorkflow.canLaunchDevices, true);
+    expect(macOSWorkflow.canListEmulators, false);
+  }));
+
+  test('Does not apply to non-macOS platform', () => testbed.run(() {
+    expect(macOSWorkflow.appliesToHostPlatform, false);
+    expect(macOSWorkflow.canListDevices, false);
+    expect(macOSWorkflow.canLaunchDevices, false);
+    expect(macOSWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    Platform: () => notMac,
+  }));
+
+  test('Does not apply when feature is disabled', () => testbed.run(() {
+    expect(macOSWorkflow.appliesToHostPlatform, false);
+    expect(macOSWorkflow.canListDevices, false);
+    expect(macOSWorkflow.canLaunchDevices, false);
+    expect(macOSWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: false),
+  }));
 }
 
 class MockPlatform extends Mock implements Platform {
diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
index c781e3c..eb06c9d 100644
--- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
+++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
@@ -243,27 +243,6 @@
       SystemClock: () => clock,
       Usage: () => usage,
     });
-
-  });
-
-  group('Experimental commands', () {
-    final MockVersion stableVersion = MockVersion();
-    final MockVersion betaVersion = MockVersion();
-    final FakeCommand fakeCommand = FakeCommand();
-    when(stableVersion.isMaster).thenReturn(false);
-    when(betaVersion.isMaster).thenReturn(true);
-
-    testUsingContext('Can be disabled on stable branch', () async {
-      expect(() => fakeCommand.run(), throwsA(isA<ToolExit>()));
-    }, overrides: <Type, Generator>{
-      FlutterVersion: () => stableVersion,
-    });
-
-    testUsingContext('Works normally on regular branches', () async {
-      expect(fakeCommand.run(), completes);
-    }, overrides: <Type, Generator>{
-      FlutterVersion: () => betaVersion,
-    });
   });
 }
 
@@ -276,9 +255,6 @@
   String get name => 'fake';
 
   @override
-  bool get isExperimental => true;
-
-  @override
   Future<FlutterCommandResult> runCommand() async {
     return null;
   }
diff --git a/packages/flutter_tools/test/general.shard/web/workflow_test.dart b/packages/flutter_tools/test/general.shard/web/workflow_test.dart
index 86a8d6b..ab11aea 100644
--- a/packages/flutter_tools/test/general.shard/web/workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/workflow_test.dart
@@ -4,7 +4,7 @@
 
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/platform.dart';
-import 'package:flutter_tools/src/version.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/web/chrome.dart';
 import 'package:flutter_tools/src/web/workflow.dart';
 import 'package:mockito/mockito.dart';
@@ -22,13 +22,9 @@
     MockPlatform linux;
     MockPlatform macos;
     MockProcessManager mockProcessManager;
-    MockFlutterVersion unstable;
-    MockFlutterVersion stable;
     WebWorkflow workflow;
 
     setUpAll(() {
-      unstable = MockFlutterVersion(false);
-      stable = MockFlutterVersion(true);
       notSupported = MockPlatform(linux: false, windows: false, macos: false);
       windows = MockPlatform(windows: true);
       linux = MockPlatform(linux: true);
@@ -39,7 +35,7 @@
         fs.file('chrome').createSync();
         when(mockProcessManager.canRun('chrome')).thenReturn(true);
       }, overrides: <Type, Generator>{
-        FlutterVersion: () => unstable,
+        FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
         ProcessManager: () => mockProcessManager,
       });
     });
@@ -81,27 +77,18 @@
       Platform: () => notSupported,
     }));
 
-    test('does not apply on stable branch', () => testbed.run(() {
+    test('does not apply if feature flag is disabled', () => testbed.run(() {
       expect(workflow.appliesToHostPlatform, false);
       expect(workflow.canLaunchDevices, false);
       expect(workflow.canListDevices, false);
       expect(workflow.canListEmulators, false);
     }, overrides: <Type, Generator>{
       Platform: () => macos,
-      FlutterVersion: () => stable,
+      FeatureFlags: () => TestFeatureFlags(isWebEnabled: false),
     }));
   });
 }
 
-class MockFlutterVersion extends Mock implements FlutterVersion {
-  MockFlutterVersion(this.isStable);
-
-  final bool isStable;
-
-  @override
-  bool get isMaster => !isStable;
-}
-
 class MockProcessManager extends Mock implements ProcessManager {}
 
 class MockPlatform extends Mock implements Platform {
diff --git a/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart b/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart
index 4c9d202..86a566b 100644
--- a/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/windows/windows_workflow_test.dart
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import 'package:flutter_tools/src/features.dart';
 import 'package:mockito/mockito.dart';
 
 import 'package:flutter_tools/src/base/platform.dart';
@@ -9,36 +10,50 @@
 
 import '../../src/common.dart';
 import '../../src/context.dart';
+import '../../src/testbed.dart';
 
 void main() {
-  group(WindowsWorkflow, () {
-    final MockPlatform windows = MockPlatform();
-    final MockPlatform windowsWithFde = MockPlatform()
-      ..environment['ENABLE_FLUTTER_DESKTOP'] = 'true';
-    final MockPlatform notWindows = MockPlatform();
+  Testbed testbed;
+  MockPlatform windows;
+  MockPlatform notWindows;
+
+  setUp(() {
+    windows = MockPlatform();
+    notWindows = MockPlatform();
     when(windows.isWindows).thenReturn(true);
-    when(windowsWithFde.isWindows).thenReturn(true);
     when(notWindows.isWindows).thenReturn(false);
-
-    testUsingContext('Applies to windows platform', () {
-      expect(windowsWorkflow.appliesToHostPlatform, true);
-    }, overrides: <Type, Generator>{
-      Platform: () => windows,
-    });
-    testUsingContext('Does not apply to non-windows platform', () {
-      expect(windowsWorkflow.appliesToHostPlatform, false);
-    }, overrides: <Type, Generator>{
-      Platform: () => notWindows,
-    });
-
-    testUsingContext('defaults', () {
-      expect(windowsWorkflow.canListEmulators, false);
-      expect(windowsWorkflow.canLaunchDevices, true);
-      expect(windowsWorkflow.canListDevices, true);
-    }, overrides: <Type, Generator>{
-      Platform: () => windowsWithFde,
-    });
+    testbed = Testbed(
+      overrides: <Type, Generator>{
+        Platform: () => windows,
+        FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
+      }
+    );
   });
+
+  test('Windows default workflow values', () => testbed.run(() {
+    expect(windowsWorkflow.appliesToHostPlatform, true);
+    expect(windowsWorkflow.canListDevices, true);
+    expect(windowsWorkflow.canLaunchDevices, true);
+    expect(windowsWorkflow.canListEmulators, false);
+  }));
+
+  test('Windows defaults on non-windows platform', () => testbed.run(() {
+    expect(windowsWorkflow.appliesToHostPlatform, false);
+    expect(windowsWorkflow.canListDevices, false);
+    expect(windowsWorkflow.canLaunchDevices, false);
+    expect(windowsWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    Platform: () => notWindows,
+  }));
+
+  test('Windows defaults on non-windows platform', () => testbed.run(() {
+    expect(windowsWorkflow.appliesToHostPlatform, false);
+    expect(windowsWorkflow.canListDevices, false);
+    expect(windowsWorkflow.canLaunchDevices, false);
+    expect(windowsWorkflow.canListEmulators, false);
+  }, overrides: <Type, Generator>{
+    FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
+  }));
 }
 
 class MockPlatform extends Mock implements Platform {
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index 2b8f22a..3a3a4ea 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -5,8 +5,6 @@
 import 'dart:async';
 
 import 'package:args/command_runner.dart';
-import 'package:flutter_tools/src/desktop.dart';
-import 'package:flutter_tools/src/web/workflow.dart';
 import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
 import 'package:test_api/test_api.dart' as test_package show TypeMatcher;
 
@@ -20,19 +18,6 @@
 
 export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim.
 
-/// Disable both web and desktop to make testing easier. For example, prevent
-/// them from showing up in the devices list if the host happens to be setup
-/// properly.
-set debugDisableWebAndDesktop(bool value) {
-  if (value) {
-    debugDisableDesktop = true;
-    debugDisableWeb = true;
-  } else {
-    debugDisableDesktop = false;
-    debugDisableWeb = false;
-  }
-}
-
 /// A matcher that compares the type of the actual value to the type argument T.
 // TODO(ianh): Remove this once https://github.com/dart-lang/matcher/issues/98 is fixed
 Matcher isInstanceOf<T>() => test_package.TypeMatcher<T>();
diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart
index ac4901c..c0a6873 100644
--- a/packages/flutter_tools/test/src/testbed.dart
+++ b/packages/flutter_tools/test/src/testbed.dart
@@ -17,6 +17,7 @@
 import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/context_runner.dart';
+import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/reporting/usage.dart';
 import 'package:flutter_tools/src/version.dart';
 
@@ -687,3 +688,26 @@
     return null;
   }
 }
+
+// A test implementation of [FeatureFlags] that allows enabling without reading
+// config. If not otherwise specified, all values default to false.
+class TestFeatureFlags implements FeatureFlags {
+  TestFeatureFlags({
+    this.isLinuxEnabled = false,
+    this.isMacOSEnabled = false,
+    this.isWebEnabled = false,
+    this.isWindowsEnabled = false,
+});
+
+  @override
+  final bool isLinuxEnabled;
+
+  @override
+  final bool isMacOSEnabled;
+
+  @override
+  final bool isWebEnabled;
+
+  @override
+  final bool isWindowsEnabled;
+}