| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // @dart = 2.8 |
| |
| import 'package:meta/meta.dart'; |
| |
| import 'base/config.dart'; |
| import 'base/context.dart'; |
| import 'base/platform.dart'; |
| import 'version.dart'; |
| |
| /// The current [FeatureFlags] implementation. |
| /// |
| /// If not injected, a default implementation is provided. |
| FeatureFlags get featureFlags => context.get<FeatureFlags>(); |
| |
| /// The interface used to determine if a particular [Feature] is enabled. |
| /// |
| /// The rest of the tools code should use this class instead of looking up |
| /// features directly. To facilitate rolls to google3 and other clients, all |
| /// flags should be provided with a default implementation here. Clients that |
| /// use this class should extent instead of implement, so that new flags are |
| /// picked up automatically. |
| abstract class FeatureFlags { |
| /// const constructor so that subclasses can be const. |
| const FeatureFlags(); |
| |
| /// Whether flutter desktop for linux is enabled. |
| bool get isLinuxEnabled => false; |
| |
| /// Whether flutter desktop for macOS is enabled. |
| bool get isMacOSEnabled => false; |
| |
| /// Whether flutter web is enabled. |
| bool get isWebEnabled => false; |
| |
| /// Whether flutter desktop for Windows is enabled. |
| bool get isWindowsEnabled => false; |
| |
| /// Whether android is enabled. |
| bool get isAndroidEnabled => true; |
| |
| /// Whether iOS is enabled. |
| bool get isIOSEnabled => true; |
| |
| /// Whether fuchsia is enabled. |
| bool get isFuchsiaEnabled => true; |
| |
| /// Whether fast single widget reloads are enabled. |
| bool get isSingleWidgetReloadEnabled => false; |
| |
| /// Whether the CFE experimental invalidation strategy is enabled. |
| bool get isExperimentalInvalidationStrategyEnabled => false; |
| |
| /// Whether a particular feature is enabled for the current channel. |
| /// |
| /// Prefer using one of the specific getters above instead of this API. |
| bool isEnabled(Feature feature) => false; |
| } |
| |
| class FlutterFeatureFlags implements FeatureFlags { |
| FlutterFeatureFlags({ |
| @required FlutterVersion flutterVersion, |
| @required Config config, |
| @required Platform platform, |
| }) : _flutterVersion = flutterVersion, |
| _config = config, |
| _platform = platform; |
| |
| final FlutterVersion _flutterVersion; |
| final Config _config; |
| final Platform _platform; |
| |
| @override |
| bool get isLinuxEnabled => isEnabled(flutterLinuxDesktopFeature); |
| |
| @override |
| bool get isMacOSEnabled => isEnabled(flutterMacOSDesktopFeature); |
| |
| @override |
| bool get isWebEnabled => isEnabled(flutterWebFeature); |
| |
| @override |
| bool get isWindowsEnabled => isEnabled(flutterWindowsDesktopFeature); |
| |
| @override |
| bool get isAndroidEnabled => isEnabled(flutterAndroidFeature); |
| |
| @override |
| bool get isIOSEnabled => isEnabled(flutterIOSFeature); |
| |
| @override |
| bool get isFuchsiaEnabled => isEnabled(flutterFuchsiaFeature); |
| |
| @override |
| bool get isSingleWidgetReloadEnabled => isEnabled(singleWidgetReload); |
| |
| @override |
| bool get isExperimentalInvalidationStrategyEnabled => isEnabled(experimentalInvalidationStrategy); |
| |
| @override |
| bool isEnabled(Feature feature) { |
| final String currentChannel = _flutterVersion.channel; |
| final FeatureChannelSetting featureSetting = feature.getSettingForChannel(currentChannel); |
| if (!featureSetting.available) { |
| return false; |
| } |
| bool isEnabled = featureSetting.enabledByDefault; |
| if (feature.configSetting != null) { |
| final bool configOverride = _config.getValue(feature.configSetting) as bool; |
| if (configOverride != null) { |
| isEnabled = configOverride; |
| } |
| } |
| if (feature.environmentOverride != null) { |
| if (_platform.environment[feature.environmentOverride]?.toLowerCase() == 'true') { |
| isEnabled = true; |
| } |
| } |
| return isEnabled; |
| } |
| } |
| |
| /// All current Flutter feature flags. |
| const List<Feature> allFeatures = <Feature>[ |
| flutterWebFeature, |
| flutterLinuxDesktopFeature, |
| flutterMacOSDesktopFeature, |
| flutterWindowsDesktopFeature, |
| singleWidgetReload, |
| flutterAndroidFeature, |
| flutterIOSFeature, |
| flutterFuchsiaFeature, |
| experimentalInvalidationStrategy, |
| ]; |
| |
| /// The [Feature] for flutter web. |
| const Feature flutterWebFeature = Feature( |
| name: 'Flutter for web', |
| configSetting: 'enable-web', |
| environmentOverride: 'FLUTTER_WEB', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: flutterNext, |
| ), |
| beta: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: flutterNext, |
| ), |
| stable: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: flutterNext, |
| ), |
| ); |
| |
| /// The [Feature] for macOS desktop. |
| const Feature flutterMacOSDesktopFeature = Feature( |
| name: 'beta-quality support for desktop on macOS', |
| configSetting: 'enable-macos-desktop', |
| environmentOverride: 'FLUTTER_MACOS', |
| extraHelpText: flutterNext ? |
| 'Newer beta versions are available on the beta channel.' : null, |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| beta: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| stable: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// The [Feature] for Linux desktop. |
| const Feature flutterLinuxDesktopFeature = Feature( |
| name: 'beta-quality support for desktop on Linux', |
| configSetting: 'enable-linux-desktop', |
| environmentOverride: 'FLUTTER_LINUX', |
| extraHelpText: flutterNext ? |
| 'Newer beta versions are available on the beta channel.' : null, |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| beta: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| stable: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// The [Feature] for Windows desktop. |
| const Feature flutterWindowsDesktopFeature = Feature( |
| name: 'beta-quality support for desktop on Windows', |
| configSetting: 'enable-windows-desktop', |
| environmentOverride: 'FLUTTER_WINDOWS', |
| extraHelpText: flutterNext ? |
| 'Newer beta versions are available on the beta channel.' : null, |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| beta: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| stable: FeatureChannelSetting( |
| available: flutterNext, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// The [Feature] for Android devices. |
| const Feature flutterAndroidFeature = Feature( |
| name: 'Flutter for Android', |
| configSetting: 'enable-android', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| beta: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| stable: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| ); |
| |
| |
| /// The [Feature] for iOS devices. |
| const Feature flutterIOSFeature = Feature( |
| name: 'Flutter for iOS', |
| configSetting: 'enable-ios', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| beta: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| stable: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| ); |
| |
| /// The [Feature] for Fuchsia support. |
| const Feature flutterFuchsiaFeature = Feature( |
| name: 'Flutter for Fuchsia', |
| configSetting: 'enable-fuchsia', |
| environmentOverride: 'FLUTTER_FUCHSIA', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// The fast hot reload feature for https://github.com/flutter/flutter/issues/61407. |
| const Feature singleWidgetReload = Feature( |
| name: 'Hot reload optimization for changes to class body of a single widget', |
| configSetting: 'single-widget-reload-optimization', |
| environmentOverride: 'FLUTTER_SINGLE_WIDGET_RELOAD', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| beta: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| stable: FeatureChannelSetting( |
| available: false, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// The CFE experimental invalidation strategy. |
| const Feature experimentalInvalidationStrategy = Feature( |
| name: 'Hot reload optimization that reduces incremental artifact size', |
| configSetting: 'experimental-invalidation-strategy', |
| environmentOverride: 'FLUTTER_CFE_EXPERIMENTAL_INVALIDATION', |
| master: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: true, |
| ), |
| dev: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| beta: FeatureChannelSetting( |
| available: true, |
| enabledByDefault: false, |
| ), |
| stable: FeatureChannelSetting( |
| available: false, |
| enabledByDefault: false, |
| ), |
| ); |
| |
| /// A [Feature] is a process for conditionally enabling tool features. |
| /// |
| /// All settings are optional, and if not provided will generally default to |
| /// a "safe" value, such as being off. |
| /// |
| /// The top level feature settings can be provided to apply to all channels. |
| /// Otherwise, more specific settings take precedence over higher level |
| /// settings. |
| class Feature { |
| /// Creates a [Feature]. |
| const Feature({ |
| @required this.name, |
| this.environmentOverride, |
| this.configSetting, |
| this.extraHelpText, |
| this.master = const FeatureChannelSetting(), |
| this.dev = const FeatureChannelSetting(), |
| this.beta = const FeatureChannelSetting(), |
| this.stable = const FeatureChannelSetting(), |
| }); |
| |
| /// The user visible name for this feature. |
| final String name; |
| |
| /// The settings for the master branch and other unknown channels. |
| final FeatureChannelSetting master; |
| |
| /// The settings for the dev branch. |
| final FeatureChannelSetting dev; |
| |
| /// The settings for the beta branch. |
| final FeatureChannelSetting beta; |
| |
| /// The settings for the stable branch. |
| final FeatureChannelSetting stable; |
| |
| /// The name of an environment variable that can override the setting. |
| /// |
| /// The environment variable needs to be set to the value 'true'. This is |
| /// only intended for usage by CI and not as an advertised method to enable |
| /// a feature. |
| /// |
| /// If not provided, defaults to `null` meaning there is no override. |
| final String environmentOverride; |
| |
| /// The name of a setting that can be used to enable this feature. |
| /// |
| /// If not provided, defaults to `null` meaning there is no config setting. |
| final String configSetting; |
| |
| /// Additional text to add to the end of the help message. |
| /// |
| /// If not provided, defaults to `null` meaning there is no additional text. |
| final String extraHelpText; |
| |
| /// A help message for the `flutter config` command, or null if unsupported. |
| String generateHelpMessage() { |
| if (configSetting == null) { |
| return null; |
| } |
| final StringBuffer buffer = StringBuffer('Enable or disable $name. ' |
| 'This setting will take effect on '); |
| final List<String> channels = <String>[ |
| if (master.available) 'master', |
| if (dev.available) 'dev', |
| if (beta.available) 'beta', |
| if (stable.available) 'stable', |
| ]; |
| if (channels.length == 1) { |
| buffer.write('the ${channels.single} channel.'); |
| } else if (channels.length == 2) { |
| buffer.write('the ${channels.join(' and ')} channels.'); |
| } else { |
| final String prefix = (channels.toList() |
| ..removeLast()).join(', '); |
| buffer.write('the $prefix, and ${channels.last} channels.'); |
| } |
| if (extraHelpText != null) { |
| buffer.write(' $extraHelpText'); |
| } |
| return buffer.toString(); |
| } |
| |
| /// Retrieve the correct setting for the provided `channel`. |
| FeatureChannelSetting getSettingForChannel(String channel) { |
| switch (channel) { |
| case 'stable': |
| return stable; |
| case 'beta': |
| return beta; |
| case 'dev': |
| return dev; |
| case 'master': |
| default: |
| return master; |
| } |
| } |
| } |
| |
| /// A description of the conditions to enable a feature for a particular channel. |
| class FeatureChannelSetting { |
| const FeatureChannelSetting({ |
| this.available = false, |
| this.enabledByDefault = false, |
| }); |
| |
| /// Whether the feature is available on this channel. |
| /// |
| /// If not provided, defaults to `false`. This implies that the feature |
| /// cannot be enabled even by the settings below. |
| final bool available; |
| |
| /// Whether the feature is enabled by default. |
| /// |
| /// If not provided, defaults to `false`. |
| final bool enabledByDefault; |
| } |
| |
| const bool flutterNext = true; |