// 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. /// @docImport 'flutter_features.dart'; library; import 'package:meta/meta.dart'; import 'base/context.dart'; /// The current [FeatureFlags] implementation. FeatureFlags get featureFlags => context.get()!; /// The interface used to determine if a particular [Feature] is enabled. /// /// This class is extended in google3. Whenever a new flag is added, /// google3 must also be updated using a g3fix. /// /// See also: /// /// * [FlutterFeatureFlags], Flutter's implementation of this class. /// * https://github.com/flutter/flutter/blob/main/docs/contributing/Feature-flags.md, /// docs on feature flags and how to add or use them. abstract class FeatureFlags { /// const constructor so that subclasses can be const. const FeatureFlags(); /// Whether flutter desktop for linux is enabled. bool get isLinuxEnabled; /// Whether flutter desktop for macOS is enabled. bool get isMacOSEnabled; /// Whether flutter web is enabled. bool get isWebEnabled; /// Whether flutter desktop for Windows is enabled. bool get isWindowsEnabled; /// Whether android is enabled. bool get isAndroidEnabled; /// Whether iOS is enabled. bool get isIOSEnabled; /// Whether fuchsia is enabled. bool get isFuchsiaEnabled; /// Whether custom devices are enabled. bool get areCustomDevicesEnabled; /// Whether animations are used in the command line interface. bool get isCliAnimationEnabled; /// Whether native assets compilation and bundling is enabled. bool get isNativeAssetsEnabled; /// Whether dart data assets building and bundling is enabled. bool get isDartDataAssetsEnabled => false; /// Whether Swift Package Manager dependency management is enabled. bool get isSwiftPackageManagerEnabled; /// Whether to stop writing the `{FLUTTER_ROOT}/version` file. /// /// Tracking removal: . bool get isOmitLegacyVersionFileEnabled; /// Whether desktop windowing is enabled. bool get isWindowingEnabled; /// Whether physical iOS devices are debugging with LLDB. bool get isLLDBDebuggingEnabled; /// Whether UIScene migration is enabled. bool get isUISceneMigrationEnabled; /// 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); /// All current Flutter feature flags. List get allFeatures => const [ flutterWebFeature, flutterLinuxDesktopFeature, flutterMacOSDesktopFeature, flutterWindowsDesktopFeature, flutterAndroidFeature, flutterIOSFeature, flutterFuchsiaFeature, flutterCustomDevicesFeature, cliAnimation, nativeAssets, dartDataAssets, swiftPackageManager, omitLegacyVersionFile, windowingFeature, lldbDebugging, uiSceneMigration, ]; /// All current Flutter feature flags that can be configured. /// /// [Feature.configSetting] is not `null`. Iterable get allConfigurableFeatures { return allFeatures.where((Feature feature) => feature.configSetting != null); } /// All Flutter feature flags that are enabled. // This member is overriden in google3. Iterable get allEnabledFeatures { return allFeatures.where(isEnabled); } } /// All current Flutter feature flags that can be configured. /// /// [Feature.configSetting] is not `null`. Iterable get allConfigurableFeatures => featureFlags.allConfigurableFeatures; /// The [Feature] for flutter web. const flutterWebFeature = Feature.fullyEnabled( name: 'Flutter for web', configSetting: 'enable-web', environmentOverride: 'FLUTTER_WEB', ); /// The [Feature] for macOS desktop. const flutterMacOSDesktopFeature = Feature.fullyEnabled( name: 'support for desktop on macOS', configSetting: 'enable-macos-desktop', environmentOverride: 'FLUTTER_MACOS', ); /// The [Feature] for Linux desktop. const flutterLinuxDesktopFeature = Feature.fullyEnabled( name: 'support for desktop on Linux', configSetting: 'enable-linux-desktop', environmentOverride: 'FLUTTER_LINUX', ); /// The [Feature] for Windows desktop. const flutterWindowsDesktopFeature = Feature.fullyEnabled( name: 'support for desktop on Windows', configSetting: 'enable-windows-desktop', environmentOverride: 'FLUTTER_WINDOWS', ); /// The [Feature] for Android devices. const flutterAndroidFeature = Feature.fullyEnabled( name: 'Flutter for Android', configSetting: 'enable-android', ); /// The [Feature] for iOS devices. const flutterIOSFeature = Feature.fullyEnabled( name: 'Flutter for iOS', configSetting: 'enable-ios', ); /// The [Feature] for Fuchsia support. const flutterFuchsiaFeature = Feature( name: 'Flutter for Fuchsia', configSetting: 'enable-fuchsia', environmentOverride: 'FLUTTER_FUCHSIA', master: FeatureChannelSetting(available: true), ); const flutterCustomDevicesFeature = Feature( name: 'early support for custom device types', configSetting: 'enable-custom-devices', environmentOverride: 'FLUTTER_CUSTOM_DEVICES', master: FeatureChannelSetting(available: true), beta: FeatureChannelSetting(available: true), stable: FeatureChannelSetting(available: true), ); /// The [Feature] for CLI animations. /// /// The TERM environment variable set to "dumb" turns this off. const cliAnimation = Feature.fullyEnabled( name: 'animations in the command line interface', configSetting: 'cli-animations', ); /// Enable native assets compilation and bundling. const nativeAssets = Feature( name: 'native assets compilation and bundling', configSetting: 'enable-native-assets', environmentOverride: 'FLUTTER_NATIVE_ASSETS', master: FeatureChannelSetting(available: true, enabledByDefault: true), beta: FeatureChannelSetting(available: true, enabledByDefault: true), stable: FeatureChannelSetting(available: true, enabledByDefault: true), ); /// Enable Dart data assets building and bundling. const dartDataAssets = Feature( name: 'Dart data assets building and bundling', configSetting: 'enable-dart-data-assets', environmentOverride: 'FLUTTER_DART_DATA_ASSETS', master: FeatureChannelSetting(available: true), ); /// Enable Swift Package Manager as a darwin dependency manager. const swiftPackageManager = Feature( name: 'support for Swift Package Manager for iOS and macOS', configSetting: 'enable-swift-package-manager', environmentOverride: 'FLUTTER_SWIFT_PACKAGE_MANAGER', master: FeatureChannelSetting(available: true), beta: FeatureChannelSetting(available: true), stable: FeatureChannelSetting(available: true), ); /// Whether to continue writing the `{FLUTTER_ROOT}/version` legacy file. /// /// Tracking removal: . const omitLegacyVersionFile = Feature.fullyEnabled( name: 'stops writing the legacy version file', configSetting: 'omit-legacy-version-file', extraHelpText: 'If set, the file {FLUTTER_ROOT}/version is no longer written as part of ' 'the flutter tool execution; a newer file format has existed for some ' 'time in {FLUTTER_ROOT}/bin/cache/flutter.version.json.', ); /// Whether desktop windowing is enabled. /// /// See: https://github.com/flutter/flutter/issues/30701. const windowingFeature = Feature( name: 'support for windowing on macOS, Linux, and Windows', configSetting: 'enable-windowing', environmentOverride: 'FLUTTER_WINDOWING', runtimeId: 'windowing', master: FeatureChannelSetting(available: true), ); /// Enable LLDB debugging for physical iOS devices. When LLDB debugging is off, /// Xcode debugging is used instead. /// /// Requires iOS 17+ and Xcode 26+. If those requirements are not met, the previous /// default debugging method is used instead. const lldbDebugging = Feature( name: 'support for debugging with LLDB for physical iOS devices', extraHelpText: 'If LLDB debugging is off, Xcode debugging is used instead. ' 'Only available for iOS 17 or newer devices. Requires Xcode 26 or greater.', configSetting: 'enable-lldb-debugging', environmentOverride: 'FLUTTER_LLDB_DEBUGGING', master: FeatureChannelSetting(available: true, enabledByDefault: true), beta: FeatureChannelSetting(available: true, enabledByDefault: true), stable: FeatureChannelSetting(available: true, enabledByDefault: true), ); /// Enable UIScene lifecycle migration for iOS apps. When enabled, if possible the tool will /// attempt to auto-migrate the app. Otherwise, it will print a warning with instructions on how to /// migrate manually. const uiSceneMigration = Feature( name: 'support for migrating to UIScene lifecycle', extraHelpText: 'If enabled, Flutter will migrate your app to iOS UIScene lifecycle if possible or ' 'otherwise instruct you to migrate manually.', configSetting: 'enable-uiscene-migration', environmentOverride: 'FLUTTER_UISCENE_MIGRATION', master: FeatureChannelSetting(available: true, enabledByDefault: true), beta: FeatureChannelSetting(available: true, enabledByDefault: true), stable: FeatureChannelSetting(available: true, enabledByDefault: true), ); /// 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.runtimeId, this.extraHelpText, this.master = const FeatureChannelSetting(), this.beta = const FeatureChannelSetting(), this.stable = const FeatureChannelSetting(), }); /// Creates a [Feature] that is fully enabled across channels. const Feature.fullyEnabled({ required this.name, this.environmentOverride, this.configSetting, this.runtimeId, this.extraHelpText, }) : master = const FeatureChannelSetting(available: true, enabledByDefault: true), beta = const FeatureChannelSetting(available: true, enabledByDefault: true), stable = const FeatureChannelSetting(available: true, enabledByDefault: true); /// 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 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; /// The unique identifier for this feature at runtime. /// /// If not `null`, the Flutter framework's enabled feature flags will /// contain this value if this feature is enabled. final String? runtimeId; /// 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 buffer = StringBuffer('Enable or disable $name.'); final channels = [ if (master.available) 'master', if (beta.available) 'beta', if (stable.available) 'stable', ]; // Add channel info for settings only on some channels. if (channels.length == 1) { buffer.write('\nThis setting applies only to the ${channels.single} channel.'); } else if (channels.length == 2) { buffer.write('\nThis setting applies only to the ${channels.join(' and ')} channels.'); } if (extraHelpText != null) { buffer.write(' $extraHelpText'); } return buffer.toString(); } /// Retrieve the correct setting for the provided `channel`. FeatureChannelSetting getSettingForChannel(String channel) { return switch (channel) { 'stable' => stable, 'beta' => beta, 'master' || _ => master, }; } } /// A description of the conditions to enable a feature for a particular channel. @immutable final 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; @override bool operator ==(Object other) { return other is FeatureChannelSetting && available == other.available && enabledByDefault == other.enabledByDefault; } @override int get hashCode => Object.hash(available, enabledByDefault); @override String toString() { return 'FeatureChannelSetting '; } }