From b1d75fc99dc9bb5f64f8ed0cb563089011aa2aae Mon Sep 17 00:00:00 2001
From: Jonah Williams <jonahwilliams@google.com>
Date: Wed, 3 Jun 2020 21:02:07 -0700
Subject: [PATCH] [flutter_tools] add flag for sound-null-safety, unify with
 experiments (#58533)

Also combines experiments into extraGenSnapshot/ExtraFrontEndOptions. Allows providing --no-sound-null-safety to allow out of order migration and running.
---
 dev/bots/test.dart                            | 16 +++++------
 .../flutter_tools/bin/fuchsia_tester.dart     |  2 +-
 packages/flutter_tools/bin/macos_assemble.sh  |  2 +-
 packages/flutter_tools/bin/tool_backend.dart  |  2 +-
 packages/flutter_tools/bin/xcode_backend.sh   |  2 +-
 packages/flutter_tools/gradle/flutter.gradle  |  6 ++---
 .../flutter_tools/lib/src/android/gradle.dart |  4 +--
 .../flutter_tools/lib/src/build_info.dart     | 10 +++----
 .../src/build_runner/resident_web_runner.dart |  2 --
 .../lib/src/build_system/targets/android.dart |  3 +--
 .../lib/src/build_system/targets/dart.dart    | 10 +++----
 .../lib/src/build_system/targets/ios.dart     | 12 +--------
 .../lib/src/build_system/targets/macos.dart   |  3 +--
 .../lib/src/build_system/targets/web.dart     | 15 +++--------
 .../lib/src/commands/assemble.dart            |  5 +++-
 .../lib/src/commands/build_aar.dart           |  1 +
 .../lib/src/commands/build_apk.dart           |  1 +
 .../lib/src/commands/build_fuchsia.dart       |  1 +
 .../lib/src/commands/build_ios.dart           |  1 +
 .../lib/src/commands/build_linux.dart         |  1 +
 .../lib/src/commands/build_macos.dart         |  1 +
 .../lib/src/commands/build_web.dart           |  4 +--
 .../flutter_tools/lib/src/commands/run.dart   |  1 +
 .../flutter_tools/lib/src/commands/test.dart  |  8 +++---
 packages/flutter_tools/lib/src/compile.dart   | 25 ++++++++++++-----
 .../lib/src/resident_runner.dart              |  8 +++---
 .../lib/src/runner/flutter_command.dart       | 27 ++++++++++++++++---
 .../lib/src/test/flutter_platform.dart        | 12 ++++-----
 .../flutter_tools/lib/src/test/runner.dart    |  6 ++---
 .../lib/src/test/test_compiler.dart           |  6 ++---
 .../flutter_tools/lib/src/web/compile.dart    |  5 ++--
 .../hermetic/build_linux_test.dart            |  4 +--
 .../hermetic/build_web_test.dart              |  1 -
 .../hermetic/build_windows_test.dart          |  4 +--
 .../commands.shard/hermetic/test_test.dart    |  2 +-
 .../test/general.shard/build_info_test.dart   | 14 +++++-----
 .../build_system/targets/web_test.dart        |  2 +-
 .../general.shard/flutter_platform_test.dart  | 12 ++++-----
 38 files changed, 127 insertions(+), 114 deletions(-)

diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index d4598c95ff..815522a658 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -340,8 +340,7 @@ Future<void> _runToolTests() async {
 /// target app.
 Future<void> _runBuildTests() async {
   final List<FileSystemEntity> exampleDirectories = Directory(path.join(flutterRoot, 'examples')).listSync()
-    // TODO(jonahwilliams): re-enable once https://github.com/flutter/flutter/issues/57234 is done.
-    // ..add(Directory(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable')))
+    ..add(Directory(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable')))
     ..add(Directory(path.join(flutterRoot, 'dev', 'integration_tests', 'flutter_gallery')));
 
   final String branch = Platform.environment['CIRRUS_BRANCH'];
@@ -377,7 +376,7 @@ Future<void> _runExampleProjectBuildTests(FileSystemEntity exampleDirectory) asy
   final String examplePath = exampleDirectory.path;
   final bool hasNullSafety = File(path.join(examplePath, 'null_safety')).existsSync();
   final List<String> additionalArgs = hasNullSafety
-    ? <String>['--enable-experiment', 'non-nullable']
+    ? <String>['--enable-experiment', 'non-nullable', '--no-sound-null-safety']
     : <String>[];
   if (Directory(path.join(examplePath, 'android')).existsSync()) {
     await _flutterBuildApk(examplePath, release: false, additionalArgs: additionalArgs, verifyCaching: verifyCaching);
@@ -603,10 +602,9 @@ Future<void> _runFrameworkTests() async {
     await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), tableData: bigqueryApi?.tabledata);
-    // TODO(jonahwilliams): re-enable once https://github.com/flutter/flutter/issues/57234 is done.
-    // await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable'),
-    //   options: <String>['--enable-experiment=non-nullable'],
-    // );
+    await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'non_nullable'),
+      options: <String>['--enable-experiment=non-nullable', '--no-sound-null-safety'],
+    );
     await _runFlutterTest(
       path.join(flutterRoot, 'dev', 'tracing_tests'),
       options: <String>['--enable-vmservice'],
@@ -756,8 +754,7 @@ Future<void> _runWebIntegrationTests() async {
   await _runWebDebugTest('lib/stack_trace.dart');
   await _runWebDebugTest('lib/web_directory_loading.dart');
   await _runWebDebugTest('test/test.dart');
-  // TODO(jonahwilliams): re-enable once https://github.com/flutter/flutter/issues/57234 is done.
-  // await _runWebDebugTest('lib/null_safe_main.dart', enableNullSafety: true);
+  await _runWebDebugTest('lib/null_safe_main.dart', enableNullSafety: true);
   await _runWebDebugTest('lib/web_define_loading.dart',
     additionalArguments: <String>[
       '--dart-define=test.valueA=Example',
@@ -875,6 +872,7 @@ Future<void> _runWebDebugTest(String target, {
         ...<String>[
           '--enable-experiment',
           'non-nullable',
+          '--no-sound-null-safety'
         ],
       '-d',
       'chrome',
diff --git a/packages/flutter_tools/bin/fuchsia_tester.dart b/packages/flutter_tools/bin/fuchsia_tester.dart
index 53fecaed23..fea43d7f73 100644
--- a/packages/flutter_tools/bin/fuchsia_tester.dart
+++ b/packages/flutter_tools/bin/fuchsia_tester.dart
@@ -155,7 +155,7 @@ Future<void> run(List<String> args) async {
       concurrency: math.max(1, globals.platform.numberOfProcessors - 2),
       icudtlPath: globals.fs.path.absolute(argResults[_kOptionIcudtl] as String),
       coverageDirectory: coverageDirectory,
-      dartExperiments: <String>[],
+      extraFrontEndOptions: <String>[],
     );
 
     if (collector != null) {
diff --git a/packages/flutter_tools/bin/macos_assemble.sh b/packages/flutter_tools/bin/macos_assemble.sh
index 20e4cccc3a..b6aaf14be0 100755
--- a/packages/flutter_tools/bin/macos_assemble.sh
+++ b/packages/flutter_tools/bin/macos_assemble.sh
@@ -82,7 +82,7 @@ RunCommand "${FLUTTER_ROOT}/bin/flutter"                                    \
     -dTrackWidgetCreation="${TRACK_WIDGET_CREATION}"                        \
     --DartDefines="${DART_DEFINES}"                                         \
     --ExtraGenSnapshotOptions="${EXTRA_GEN_SNAPSHOT_OPTIONS}"               \
-    -dExtraFrontEndOptions="${EXTRA_FRONT_END_OPTIONS}"                     \
+    --ExtraFrontEndOptions="${EXTRA_FRONT_END_OPTIONS}"                     \
     --build-inputs="${build_inputs_path}"                                   \
     --build-outputs="${build_outputs_path}"                                 \
     --output="${ephemeral_dir}"                                             \
diff --git a/packages/flutter_tools/bin/tool_backend.dart b/packages/flutter_tools/bin/tool_backend.dart
index 4f55437fea..5f0554e2d1 100644
--- a/packages/flutter_tools/bin/tool_backend.dart
+++ b/packages/flutter_tools/bin/tool_backend.dart
@@ -69,7 +69,7 @@ or
       if (extraGenSnapshotOptions != null)
         '--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
       if (extraFrontEndOptions != null)
-        '-dExtraFrontEndOptions=$extraFrontEndOptions',
+        '--ExtraFrontEndOptions=$extraFrontEndOptions',
       target,
     ],
   );
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
index dd697a6dcc..98812435f7 100755
--- a/packages/flutter_tools/bin/xcode_backend.sh
+++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -168,7 +168,7 @@ is set to release or run \"flutter build ios --release\", then re-run Archive fr
     -dEnableBitcode="${bitcode_flag}"                                     \
     --ExtraGenSnapshotOptions="${EXTRA_GEN_SNAPSHOT_OPTIONS}"             \
     --DartDefines="${DART_DEFINES}"                                       \
-    -dExtraFrontEndOptions="${EXTRA_FRONT_END_OPTIONS}"                   \
+    --ExtraFrontEndOptions="${EXTRA_FRONT_END_OPTIONS}"                   \
     "${build_mode}_ios_bundle_flutter_assets"
 
   if [[ $? -ne 0 ]]; then
diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle
index 1d7100b9d2..202e37b7ca 100644
--- a/packages/flutter_tools/gradle/flutter.gradle
+++ b/packages/flutter_tools/gradle/flutter.gradle
@@ -919,9 +919,6 @@ abstract class BaseFlutterTask extends DefaultTask {
             }
             args "-dTargetPlatform=android"
             args "-dBuildMode=${buildMode}"
-            if (extraFrontEndOptions != null) {
-                args "-dExtraFrontEndOptions=${extraFrontEndOptions}"
-            }
             if (trackWidgetCreation != null) {
                 args "-dTrackWidgetCreation=${trackWidgetCreation}"
             }
@@ -943,6 +940,9 @@ abstract class BaseFlutterTask extends DefaultTask {
             if (extraGenSnapshotOptions != null) {
                 args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
             }
+            if (extraFrontEndOptions != null) {
+                args "--ExtraFrontEndOptions=${extraFrontEndOptions}"
+            }
             args ruleNames
         }
     }
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart
index 43652520ee..dcc5bb0bbd 100644
--- a/packages/flutter_tools/lib/src/android/gradle.dart
+++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -310,10 +310,10 @@ Future<void> buildGradleApp({
   command.add('-Ptrack-widget-creation=${buildInfo.trackWidgetCreation}');
 
   if (buildInfo.extraFrontEndOptions != null) {
-    command.add('-Pextra-front-end-options=${buildInfo.extraFrontEndOptions.join(',')}');
+    command.add('-Pextra-front-end-options=${encodeDartDefines(buildInfo.extraFrontEndOptions)}');
   }
   if (buildInfo.extraGenSnapshotOptions != null) {
-    command.add('-Pextra-gen-snapshot-options=${buildInfo.extraGenSnapshotOptions.join(',')}');
+    command.add('-Pextra-gen-snapshot-options=${encodeDartDefines(buildInfo.extraGenSnapshotOptions)}');
   }
   if (buildInfo.fileSystemRoots != null && buildInfo.fileSystemRoots.isNotEmpty) {
     command.add('-Pfilesystem-roots=${buildInfo.fileSystemRoots.join('|')}');
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 99af2f20a1..792a360f6a 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -143,9 +143,9 @@ class BuildInfo {
       if (dartObfuscation != null)
         'DART_OBFUSCATION': dartObfuscation.toString(),
       if (extraFrontEndOptions?.isNotEmpty ?? false)
-        'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions.join(','),
+        'EXTRA_FRONT_END_OPTIONS': encodeDartDefines(extraFrontEndOptions),
       if (extraGenSnapshotOptions?.isNotEmpty ?? false)
-        'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions.join(','),
+        'EXTRA_GEN_SNAPSHOT_OPTIONS': encodeDartDefines(extraGenSnapshotOptions),
       if (splitDebugInfoPath != null)
         'SPLIT_DEBUG_INFO': splitDebugInfoPath,
       if (trackWidgetCreation != null)
@@ -667,11 +667,11 @@ String encodeDartDefines(List<String> defines) {
 }
 
 /// Dart defines are encoded inside [environmentDefines] as a comma-separated list.
-List<String> decodeDartDefines(Map<String, String> environmentDefines) {
-  if (!environmentDefines.containsKey(kDartDefines) || environmentDefines[kDartDefines].isEmpty) {
+List<String> decodeDartDefines(Map<String, String> environmentDefines, String key) {
+  if (!environmentDefines.containsKey(key) || environmentDefines[key].isEmpty) {
     return const <String>[];
   }
-  return environmentDefines[kDartDefines]
+  return environmentDefines[key]
     .split(',')
     .map<Object>(Uri.decodeComponent)
     .cast<String>()
diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
index 0ecb2d6ec2..ba6c1a7974 100644
--- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
@@ -441,7 +441,6 @@ class _ResidentWebRunner extends ResidentWebRunner {
             debuggingOptions.buildInfo,
             debuggingOptions.initializePlatform,
             false,
-            debuggingOptions.buildInfo.dartExperiments,
           );
         }
         await device.device.startApp(
@@ -504,7 +503,6 @@ class _ResidentWebRunner extends ResidentWebRunner {
           debuggingOptions.buildInfo,
           debuggingOptions.initializePlatform,
           false,
-          debuggingOptions.buildInfo.dartExperiments,
         );
       } on ToolExit {
         return OperationResult(1, 'Failed to recompile application.');
diff --git a/packages/flutter_tools/lib/src/build_system/targets/android.dart b/packages/flutter_tools/lib/src/build_system/targets/android.dart
index bcf9327902..d2e3c6f9bb 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/android.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/android.dart
@@ -246,8 +246,7 @@ class AndroidAot extends AotElfBase {
     if (!output.existsSync()) {
       output.createSync(recursive: true);
     }
-    final List<String> extraGenSnapshotOptions = environment.defines[kExtraGenSnapshotOptions]?.split(',')
-      ?? const <String>[];
+    final List<String> extraGenSnapshotOptions = decodeDartDefines(environment.defines, kExtraGenSnapshotOptions);
     final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
     final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
     final int snapshotExitCode = await snapshotter.build(
diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
index dcd28ebea4..f29d631d6d 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
@@ -205,10 +205,7 @@ class KernelSnapshot extends Target {
     final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
 
     // This configuration is all optional.
-    final String rawFrontEndOption = environment.defines[kExtraFrontEndOptions];
-    final List<String> extraFrontEndOptions = (rawFrontEndOption?.isNotEmpty ?? false)
-      ? rawFrontEndOption?.split(',')
-      : null;
+    final List<String> extraFrontEndOptions = decodeDartDefines(environment.defines, kExtraFrontEndOptions);
     final List<String> fileSystemRoots = environment.defines[kFileSystemRoots]?.split(',');
     final String fileSystemScheme = environment.defines[kFileSystemScheme];
 
@@ -254,7 +251,7 @@ class KernelSnapshot extends Target {
       extraFrontEndOptions: extraFrontEndOptions,
       fileSystemRoots: fileSystemRoots,
       fileSystemScheme: fileSystemScheme,
-      dartDefines: decodeDartDefines(environment.defines),
+      dartDefines: decodeDartDefines(environment.defines, kDartDefines),
       packageConfig: packageConfig,
     );
     if (output == null || output.errorCount != 0) {
@@ -287,8 +284,7 @@ abstract class AotElfBase extends Target {
     if (environment.defines[kTargetPlatform] == null) {
       throw MissingDefineException(kTargetPlatform, 'aot_elf');
     }
-    final List<String> extraGenSnapshotOptions = environment.defines[kExtraGenSnapshotOptions]?.split(',')
-      ?? const <String>[];
+    final List<String> extraGenSnapshotOptions = decodeDartDefines(environment.defines, kExtraGenSnapshotOptions);
     final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
     final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
     final String splitDebugInfo = environment.defines[kSplitDebugInfo];
diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
index 435bb349d9..cca692cae2 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart
@@ -46,7 +46,7 @@ abstract class AotAssemblyBase extends Target {
     if (environment.defines[kTargetPlatform] == null) {
       throw MissingDefineException(kTargetPlatform, 'aot_assembly');
     }
-    final List<String> extraGenSnapshotOptions = parseExtraGenSnapshotOptions(environment);
+    final List<String> extraGenSnapshotOptions = decodeDartDefines(environment.defines, kExtraGenSnapshotOptions);
     final bool bitcode = environment.defines[kBitcodeFlag] == 'true';
     final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]);
     final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
@@ -447,13 +447,3 @@ Future<RunResult> createStubAppFramework(File outputFile, SdkType sdk, { bool in
     }
   }
 }
-
-/// iOS and macOS build scripts may pass extraGenSnapshotOptions as an empty
-/// string.
-List<String> parseExtraGenSnapshotOptions(Environment environment) {
-  final String value = environment.defines[kExtraGenSnapshotOptions];
-  if (value == null || value.trim().isEmpty) {
-    return <String>[];
-  }
-  return value.split(',');
-}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/macos.dart b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
index 5fa93a92bc..4a1f7c38b7 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart
@@ -15,7 +15,6 @@ import '../exceptions.dart';
 import 'assets.dart';
 import 'dart.dart';
 import 'icon_tree_shaker.dart';
-import 'ios.dart';
 
 /// Copy the macOS framework to the correct copy dir by invoking 'cp -R'.
 ///
@@ -198,7 +197,7 @@ class CompileMacOSFramework extends Target {
     }
     final String splitDebugInfo = environment.defines[kSplitDebugInfo];
     final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
-    final List<String> extraGenSnapshotOptions = parseExtraGenSnapshotOptions(environment);
+    final List<String> extraGenSnapshotOptions = decodeDartDefines(environment.defines, kExtraGenSnapshotOptions);
     final AOTSnapshotter snapshotter = AOTSnapshotter(
       reportTimings: false,
       fileSystem: globals.fs,
diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart
index 2d500cec48..1cb9703296 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/web.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart
@@ -28,11 +28,6 @@ const String kHasWebPlugins = 'HasWebPlugins';
 /// Valid values are O1 (lowest, profile default) to O4 (highest, release default).
 const String kDart2jsOptimization = 'Dart2jsOptimization';
 
-/// Allow specifying experiments for dart2js.
-///
-/// Multiple values should be encoded as a comma-separated list.
-const String kEnableExperiment = 'EnableExperiment';
-
 /// Whether to disable dynamic generation code to satisfy csp policies.
 const String kCspMode = 'cspMode';
 
@@ -164,8 +159,8 @@ class Dart2JSTarget extends Target {
     final String packageFile = globalPackagesPath;
     final File outputKernel = environment.buildDir.childFile('app.dill');
     final File outputFile = environment.buildDir.childFile('main.dart.js');
-    final List<String> dartDefines = decodeDartDefines(environment.defines);
-    final String enabledExperiments = environment.defines[kEnableExperiment];
+    final List<String> dartDefines = decodeDartDefines(environment.defines, kDartDefines);
+    final List<String> extraFrontEndOptions = decodeDartDefines(environment.defines, kExtraFrontEndOptions);
 
     // Run the dart2js compilation in two stages, so that icon tree shaking can
     // parse the kernel file for web builds.
@@ -173,8 +168,7 @@ class Dart2JSTarget extends Target {
       globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
       globals.artifacts.getArtifactPath(Artifact.dart2jsSnapshot),
       '--libraries-spec=$specPath',
-      if (enabledExperiments != null)
-        '--enable-experiment=$enabledExperiments',
+      ...?extraFrontEndOptions,
       '-o',
       outputKernel.path,
       '--packages=$packageFile',
@@ -194,8 +188,7 @@ class Dart2JSTarget extends Target {
       globals.artifacts.getArtifactPath(Artifact.engineDartBinary),
       globals.artifacts.getArtifactPath(Artifact.dart2jsSnapshot),
       '--libraries-spec=$specPath',
-      if (enabledExperiments != null)
-        '--enable-experiment=$enabledExperiments',
+      ...?extraFrontEndOptions,
       if (dart2jsOptimization != null)
         '-$dart2jsOptimization'
       else
diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart
index 0c574e40cc..62c46b2d15 100644
--- a/packages/flutter_tools/lib/src/commands/assemble.dart
+++ b/packages/flutter_tools/lib/src/commands/assemble.dart
@@ -105,6 +105,7 @@ class AssembleCommand extends FlutterCommand {
         'root of the current Flutter project.',
     );
     argParser.addOption(kExtraGenSnapshotOptions);
+    argParser.addOption(kExtraFrontEndOptions);
     argParser.addOption(kDartDefines);
     argParser.addOption(
       'resource-pool-size',
@@ -204,10 +205,12 @@ class AssembleCommand extends FlutterCommand {
     if (argResults.wasParsed(kExtraGenSnapshotOptions)) {
       results[kExtraGenSnapshotOptions] = argResults[kExtraGenSnapshotOptions] as String;
     }
-    // Workaround for dart-define formatting
     if (argResults.wasParsed(kDartDefines)) {
       results[kDartDefines] = argResults[kDartDefines] as String;
     }
+    if (argResults.wasParsed(kExtraFrontEndOptions)) {
+      results[kExtraFrontEndOptions] = argResults[kExtraFrontEndOptions] as String;
+    }
     return results;
   }
 
diff --git a/packages/flutter_tools/lib/src/commands/build_aar.dart b/packages/flutter_tools/lib/src/commands/build_aar.dart
index 0ca4318ff2..5b0115f632 100644
--- a/packages/flutter_tools/lib/src/commands/build_aar.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aar.dart
@@ -41,6 +41,7 @@ class BuildAarCommand extends BuildSubCommand {
     addSplitDebugInfoOption();
     addDartObfuscationOption();
     usesTrackWidgetCreation(verboseHelp: false);
+    addNullSafetyModeOptions();
     argParser
       ..addMultiOption(
         'target-platform',
diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart
index c721302c99..9b5723aa56 100644
--- a/packages/flutter_tools/lib/src/commands/build_apk.dart
+++ b/packages/flutter_tools/lib/src/commands/build_apk.dart
@@ -33,6 +33,7 @@ class BuildApkCommand extends BuildSubCommand {
     addBundleSkSLPathOption(hide: !verboseHelp);
     addEnableExperimentation(hide: !verboseHelp);
     addBuildPerformanceFile(hide: !verboseHelp);
+    addNullSafetyModeOptions();
     argParser
       ..addFlag('split-per-abi',
         negatable: false,
diff --git a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
index ca1db5fafe..c33de7a623 100644
--- a/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
+++ b/packages/flutter_tools/lib/src/commands/build_fuchsia.dart
@@ -21,6 +21,7 @@ class BuildFuchsiaCommand extends BuildSubCommand {
     usesTargetOption();
     usesDartDefineOption();
     addBuildModeFlags(verboseHelp: verboseHelp);
+    addNullSafetyModeOptions();
     argParser.addOption(
       'runner-source',
       help: 'The package source to use for the flutter_runner. '
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart
index 5f5dee1e78..0563d70d10 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -33,6 +33,7 @@ class BuildIOSCommand extends BuildSubCommand {
     usesExtraFrontendOptions();
     addEnableExperimentation(hide: !verboseHelp);
     addBuildPerformanceFile(hide: !verboseHelp);
+    addNullSafetyModeOptions();
     argParser
       ..addFlag('simulator',
         help: 'Build for the iOS simulator instead of the device. This changes '
diff --git a/packages/flutter_tools/lib/src/commands/build_linux.dart b/packages/flutter_tools/lib/src/commands/build_linux.dart
index 3383214706..df39150cb8 100644
--- a/packages/flutter_tools/lib/src/commands/build_linux.dart
+++ b/packages/flutter_tools/lib/src/commands/build_linux.dart
@@ -28,6 +28,7 @@ class BuildLinuxCommand extends BuildSubCommand {
     addEnableExperimentation(hide: !verboseHelp);
     usesTrackWidgetCreation(verboseHelp: verboseHelp);
     addBuildPerformanceFile(hide: !verboseHelp);
+    addNullSafetyModeOptions();
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/commands/build_macos.dart b/packages/flutter_tools/lib/src/commands/build_macos.dart
index d9dd2275b7..9628ef8ab1 100644
--- a/packages/flutter_tools/lib/src/commands/build_macos.dart
+++ b/packages/flutter_tools/lib/src/commands/build_macos.dart
@@ -29,6 +29,7 @@ class BuildMacosCommand extends BuildSubCommand {
     usesBuildNameOption();
     addEnableExperimentation(hide: !verboseHelp);
     addBuildPerformanceFile(hide: !verboseHelp);
+    addNullSafetyModeOptions();
   }
 
   @override
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
index 6664f2040c..92ec741275 100644
--- a/packages/flutter_tools/lib/src/commands/build_web.dart
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -11,7 +11,7 @@ import '../build_info.dart';
 import '../features.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart'
-    show DevelopmentArtifact, FlutterCommandResult, FlutterOptions;
+    show DevelopmentArtifact, FlutterCommandResult;
 import '../web/compile.dart';
 import 'build.dart';
 
@@ -25,6 +25,7 @@ class BuildWebCommand extends BuildSubCommand {
     addBuildModeFlags(excludeDebug: true);
     usesDartDefineOption();
     addEnableExperimentation(hide: !verboseHelp);
+    addNullSafetyModeOptions();
     argParser.addFlag('web-initialize-platform',
         defaultsTo: true,
         negatable: true,
@@ -71,7 +72,6 @@ class BuildWebCommand extends BuildSubCommand {
       buildInfo,
       boolArg('web-initialize-platform'),
       boolArg('csp'),
-      stringsArg(FlutterOptions.kEnableExperiment),
     );
     return FlutterCommandResult.success();
   }
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index cbd98f38d2..a5125a8f6f 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -66,6 +66,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
     usesPubOption();
     usesTrackWidgetCreation(verboseHelp: verboseHelp);
     usesIsolateFilterOption(hide: !verboseHelp);
+    addNullSafetyModeOptions();
   }
 
   bool get traceStartup => boolArg('trace-startup');
diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index 31642bf654..7035733bcb 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -31,6 +31,9 @@ class TestCommand extends FlutterCommand {
   }) : assert(testWrapper != null) {
     requiresPubspecYaml();
     usesPubOption();
+    addNullSafetyModeOptions();
+    usesTrackWidgetCreation(verboseHelp: verboseHelp);
+    addEnableExperimentation(hide: !verboseHelp);
     argParser
       ..addMultiOption('name',
         help: 'A regular expression matching substrings of the names of tests to run.',
@@ -128,8 +131,6 @@ class TestCommand extends FlutterCommand {
               'This flag is ignored if --start-paused or coverage are requested. '
               'The vmservice will be enabled no matter what in those cases.'
       );
-    usesTrackWidgetCreation(verboseHelp: verboseHelp);
-    addEnableExperimentation(hide: !verboseHelp);
   }
 
   /// The interface for starting and configuring the tester.
@@ -172,7 +173,6 @@ class TestCommand extends FlutterCommand {
     final String tags = stringArg('tags');
     final String excludeTags = stringArg('exclude-tags');
     final FlutterProject flutterProject = FlutterProject.current();
-    final List<String> dartExperiments = stringsArg(FlutterOptions.kEnableExperiment);
 
     if (buildTestAssets && flutterProject.manifest.assets.isNotEmpty) {
       await _buildTestAsset();
@@ -278,7 +278,7 @@ class TestCommand extends FlutterCommand {
       flutterProject: flutterProject,
       web: stringArg('platform') == 'chrome',
       randomSeed: stringArg('test-randomize-ordering-seed'),
-      dartExperiments: dartExperiments,
+      extraFrontEndOptions: getBuildInfo(forcedBuildMode: BuildMode.debug).extraFrontEndOptions,
     );
 
     if (collector != null) {
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
index 879e62fe86..5a59581937 100644
--- a/packages/flutter_tools/lib/src/compile.dart
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -269,7 +269,14 @@ class KernelCompiler {
         '--platform',
         platformDill,
       ],
-      ...?extraFrontEndOptions,
+      if (extraFrontEndOptions != null)
+        for (String arg in extraFrontEndOptions)
+          if (arg == '--sound-null-safety')
+            '--null-safety'
+          else if (arg == '--no-sound-null-safety')
+            '--no-null-safety'
+          else
+            arg,
       mainUri?.toString() ?? mainPath,
     ];
 
@@ -398,7 +405,7 @@ abstract class ResidentCompiler {
     String initializeFromDill,
     TargetModel targetModel,
     bool unsafePackageSerialization,
-    List<String> experimentalFlags,
+    List<String> extraFrontEndOptions,
     String platformDill,
     List<String> dartDefines,
     String librariesSpec,
@@ -494,7 +501,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
     this.initializeFromDill,
     this.targetModel = TargetModel.flutter,
     this.unsafePackageSerialization,
-    this.experimentalFlags,
+    this.extraFrontEndOptions,
     this.platformDill,
     List<String> dartDefines,
     this.librariesSpec,
@@ -512,7 +519,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
   final String fileSystemScheme;
   final String initializeFromDill;
   final bool unsafePackageSerialization;
-  final List<String> experimentalFlags;
+  final List<String> extraFrontEndOptions;
   final List<String> dartDefines;
   final String librariesSpec;
 
@@ -660,8 +667,14 @@ class DefaultResidentCompiler implements ResidentCompiler {
         platformDill,
       ],
       if (unsafePackageSerialization == true) '--unsafe-package-serialization',
-      if ((experimentalFlags != null) && experimentalFlags.isNotEmpty)
-        '--enable-experiment=${experimentalFlags.join(',')}',
+      if (extraFrontEndOptions != null)
+        for (String arg in extraFrontEndOptions)
+          if (arg == '--sound-null-safety')
+            '--null-safety'
+          else if (arg == '--no-sound-null-safety')
+            '--no-null-safety'
+          else
+            arg,
     ];
     globals.printTrace(command.join(' '));
     _server = await globals.processManager.start(command);
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index a1522984c0..3429127fea 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -47,7 +47,6 @@ class FlutterDevice {
     this.viewFilter,
     TargetModel targetModel = TargetModel.flutter,
     TargetPlatform targetPlatform,
-    List<String> experimentalFlags,
     ResidentCompiler generator,
   }) : assert(buildInfo.trackWidgetCreation != null),
        generator = generator ?? ResidentCompiler(
@@ -61,9 +60,9 @@ class FlutterDevice {
          fileSystemRoots: fileSystemRoots ?? <String>[],
          fileSystemScheme: fileSystemScheme,
          targetModel: targetModel,
-         experimentalFlags: experimentalFlags,
          dartDefines: buildInfo.dartDefines,
          packagesPath: globalPackagesPath,
+         extraFrontEndOptions: buildInfo.extraFrontEndOptions,
        );
 
   /// Create a [FlutterDevice] with optional code generation enabled.
@@ -106,7 +105,7 @@ class FlutterDevice {
           trackWidgetCreation: buildInfo.trackWidgetCreation,
         ),
         targetModel: TargetModel.dartdevc,
-        experimentalFlags: experimentalFlags,
+        extraFrontEndOptions: buildInfo.extraFrontEndOptions,
         platformDill: globals.fs.file(globals.artifacts
           .getArtifactPath(Artifact.webPlatformKernelDill, mode: buildInfo.mode))
           .absolute.uri.toString(),
@@ -127,8 +126,8 @@ class FlutterDevice {
         fileSystemRoots: fileSystemRoots,
         fileSystemScheme: fileSystemScheme,
         targetModel: targetModel,
-        experimentalFlags: experimentalFlags,
         dartDefines: buildInfo.dartDefines,
+        extraFrontEndOptions: buildInfo.extraFrontEndOptions,
         initializeFromDill: getDefaultCachedKernelPath(
           trackWidgetCreation: buildInfo.trackWidgetCreation,
         ),
@@ -148,7 +147,6 @@ class FlutterDevice {
       fileSystemRoots: fileSystemRoots,
       fileSystemScheme:fileSystemScheme,
       viewFilter: viewFilter,
-      experimentalFlags: experimentalFlags,
       targetModel: targetModel,
       targetPlatform: targetPlatform,
       generator: generator,
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index b6de3cc450..be803bb579 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -109,6 +109,7 @@ class FlutterOptions {
   static const String kDartDefinesOption = 'dart-define';
   static const String kBundleSkSLPathOption = 'bundle-sksl-path';
   static const String kPerformanceMeasurementFile = 'performance-measurement-file';
+  static const String kNullSafety = 'sound-null-safety';
 }
 
 abstract class FlutterCommand extends Command<void> {
@@ -460,6 +461,14 @@ abstract class FlutterCommand extends Command<void> {
       );
   }
 
+  void addNullSafetyModeOptions() {
+    argParser.addFlag(FlutterOptions.kNullSafety,
+      help: 'Whether to override the default null safety setting.',
+      defaultsTo: null,
+      hide: true,
+    );
+  }
+
   void usesExtraFrontendOptions() {
     argParser.addMultiOption(FlutterOptions.kExtraFrontEndOptions,
       splitCommas: true,
@@ -572,15 +581,15 @@ abstract class FlutterCommand extends Command<void> {
 
     final List<String> experiments =
       argParser.options.containsKey(FlutterOptions.kEnableExperiment)
-        ? stringsArg(FlutterOptions.kEnableExperiment)
+        ? stringsArg(FlutterOptions.kEnableExperiment).toList()
         : <String>[];
     final List<String> extraGenSnapshotOptions =
       argParser.options.containsKey(FlutterOptions.kExtraGenSnapshotOptions)
-        ? stringsArg(FlutterOptions.kExtraGenSnapshotOptions)
+        ? stringsArg(FlutterOptions.kExtraGenSnapshotOptions).toList()
         : <String>[];
     final List<String> extraFrontEndOptions =
       argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions)
-          ? stringsArg(FlutterOptions.kExtraFrontEndOptions)
+          ? stringsArg(FlutterOptions.kExtraFrontEndOptions).toList()
           : <String>[];
 
     if (experiments.isNotEmpty) {
@@ -591,6 +600,18 @@ abstract class FlutterCommand extends Command<void> {
       }
     }
 
+    if (argParser.options.containsKey(FlutterOptions.kNullSafety)) {
+      final bool nullSafety = boolArg(FlutterOptions.kNullSafety);
+      // Explicitly check for `true` and `false` so that `null` results in not
+      // passing a flag. This will use the automatically detected null-safety
+      // value based on the entrypoint
+      if (nullSafety == true) {
+        extraFrontEndOptions.add('--sound-null-safety');
+      } else if (nullSafety == false) {
+        extraFrontEndOptions.add('--no-sound-null-safety');
+      }
+    }
+
     final bool dartObfuscation = argParser.options.containsKey(FlutterOptions.kDartObfuscationOption)
       && boolArg(FlutterOptions.kDartObfuscationOption);
 
diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart
index 7ed351e4e0..84c5687c3c 100644
--- a/packages/flutter_tools/lib/src/test/flutter_platform.dart
+++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart
@@ -88,7 +88,7 @@ FlutterPlatform installHook({
   FlutterProject flutterProject,
   String icudtlPath,
   PlatformPluginRegistration platformPluginRegistration,
-  @required List<String> dartExperiments,
+  @required List<String> extraFrontEndOptions,
 }) {
   assert(testWrapper != null);
   assert(enableObservatory || (!startPaused && observatoryPort == null));
@@ -121,7 +121,7 @@ FlutterPlatform installHook({
     projectRootDirectory: projectRootDirectory,
     flutterProject: flutterProject,
     icudtlPath: icudtlPath,
-    dartExperiments: dartExperiments,
+    extraFrontEndOptions: extraFrontEndOptions,
   );
   platformPluginRegistration(platform);
   return platform;
@@ -269,7 +269,7 @@ class FlutterPlatform extends PlatformPlugin {
     this.projectRootDirectory,
     this.flutterProject,
     this.icudtlPath,
-    @required this.dartExperiments,
+    @required this.extraFrontEndOptions,
   }) : assert(shellPath != null);
 
   final String shellPath;
@@ -290,7 +290,7 @@ class FlutterPlatform extends PlatformPlugin {
   final Uri projectRootDirectory;
   final FlutterProject flutterProject;
   final String icudtlPath;
-  final List<String> dartExperiments;
+  final List<String> extraFrontEndOptions;
 
   Directory fontsDirectory;
 
@@ -459,7 +459,7 @@ class FlutterPlatform extends PlatformPlugin {
 
       if (precompiledDillPath == null && precompiledDillFiles == null) {
         // Lazily instantiate compiler so it is built only if it is actually used.
-        compiler ??= TestCompiler(buildMode, trackWidgetCreation, flutterProject, dartExperiments);
+        compiler ??= TestCompiler(buildMode, trackWidgetCreation, flutterProject, extraFrontEndOptions);
         mainDart = await compiler.compile(globals.fs.file(mainDart).uri);
 
         if (mainDart == null) {
@@ -751,7 +751,7 @@ class FlutterPlatform extends PlatformPlugin {
       testConfigFile: findTestConfigFile(globals.fs.file(testUrl)),
       host: host,
       updateGoldens: updateGoldens,
-      nullSafety: dartExperiments.contains('non-nullable'),
+      nullSafety: extraFrontEndOptions?.contains('--enable-experiment=non-nullable') ?? false,
     );
   }
 
diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart
index cd7a6b4e42..3f3c951c08 100644
--- a/packages/flutter_tools/lib/src/test/runner.dart
+++ b/packages/flutter_tools/lib/src/test/runner.dart
@@ -51,7 +51,7 @@ abstract class FlutterTestRunner {
     Directory coverageDirectory,
     bool web = false,
     String randomSeed,
-    @required List<String> dartExperiments,
+    @required List<String> extraFrontEndOptions,
   });
 }
 
@@ -85,7 +85,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
     Directory coverageDirectory,
     bool web = false,
     String randomSeed,
-    @required List<String> dartExperiments,
+    @required List<String> extraFrontEndOptions,
   }) async {
     // Configure package:test to use the Flutter engine for child processes.
     final String shellPath = globals.artifacts.getArtifactPath(Artifact.flutterTester);
@@ -177,7 +177,7 @@ class _FlutterTestRunnerImpl implements FlutterTestRunner {
       projectRootDirectory: globals.fs.currentDirectory.uri,
       flutterProject: flutterProject,
       icudtlPath: icudtlPath,
-      dartExperiments: dartExperiments,
+      extraFrontEndOptions: extraFrontEndOptions,
     );
 
     // Make the global packages path absolute.
diff --git a/packages/flutter_tools/lib/src/test/test_compiler.dart b/packages/flutter_tools/lib/src/test/test_compiler.dart
index 9b3dc8f863..0e7f30460b 100644
--- a/packages/flutter_tools/lib/src/test/test_compiler.dart
+++ b/packages/flutter_tools/lib/src/test/test_compiler.dart
@@ -42,7 +42,7 @@ class TestCompiler {
     this.buildMode,
     this.trackWidgetCreation,
     this.flutterProject,
-    this.dartExperiments,
+    this.extraFrontEndOptions,
   ) : testFilePath = getKernelPathForTransformerOptions(
         globals.fs.path.join(flutterProject.directory.path, getBuildDirectory(), 'testfile.dill'),
         trackWidgetCreation: trackWidgetCreation,
@@ -66,7 +66,7 @@ class TestCompiler {
   final BuildMode buildMode;
   final bool trackWidgetCreation;
   final String testFilePath;
-  final List<String> dartExperiments;
+  final List<String> extraFrontEndOptions;
 
 
   ResidentCompiler compiler;
@@ -106,7 +106,7 @@ class TestCompiler {
       unsafePackageSerialization: false,
       dartDefines: const <String>[],
       packagesPath: globalPackagesPath,
-      experimentalFlags: dartExperiments,
+      extraFrontEndOptions: extraFrontEndOptions,
     );
     if (flutterProject.hasBuilders) {
       return CodeGeneratingResidentCompiler.create(
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
index 72f515a913..c103d87d31 100644
--- a/packages/flutter_tools/lib/src/web/compile.dart
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -28,7 +28,6 @@ Future<void> buildWeb(
   BuildInfo buildInfo,
   bool initializePlatform,
   bool csp,
-  List<String> experiments,
 ) async {
   if (!flutterProject.web.existsSync()) {
     throwToolExit('Missing index.html.');
@@ -53,8 +52,8 @@ Future<void> buildWeb(
         kDartDefines: encodeDartDefines(buildInfo.dartDefines),
         kCspMode: csp.toString(),
         kIconTreeShakerFlag: buildInfo.treeShakeIcons.toString(),
-        if (experiments.isNotEmpty)
-          kEnableExperiment: experiments?.join(','),
+        if (buildInfo.extraFrontEndOptions?.isNotEmpty ?? false)
+          kExtraFrontEndOptions: buildInfo.extraFrontEndOptions.join(',')
       },
       artifacts: globals.artifacts,
       fileSystem: globals.fs,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
index c88a4bdda1..483b947de5 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
@@ -356,8 +356,8 @@ void main() {
       'set(PROJECT_DIR "${fileSystem.currentDirectory.path}")',
       '  "DART_DEFINES=\\"foo.bar%3D2,fizz.far%3D3\\""',
       '  "DART_OBFUSCATION=\\"true\\""',
-      '  "EXTRA_FRONT_END_OPTIONS=\\"--enable-experiment=non-nullable\\""',
-      '  "EXTRA_GEN_SNAPSHOT_OPTIONS=\\"--enable-experiment=non-nullable\\""',
+      '  "EXTRA_FRONT_END_OPTIONS=\\"--enable-experiment%3Dnon-nullable\\""',
+      '  "EXTRA_GEN_SNAPSHOT_OPTIONS=\\"--enable-experiment%3Dnon-nullable\\""',
       '  "SPLIT_DEBUG_INFO=\\"foo/\\""',
       '  "TRACK_WIDGET_CREATION=\\"true\\""',
       '  "TREE_SHAKE_ICONS=\\"true\\""',
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
index a461f9f111..e89f79134b 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
@@ -56,7 +56,6 @@ void main() {
       BuildInfo.debug,
       false,
       false,
-      <String>[],
     ), throwsToolExit());
   }, overrides: <Type, Generator>{
     Platform: () => fakePlatform,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
index 84b653e867..c340357786 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
@@ -291,8 +291,8 @@ void main() {
     expect(props.findAllElements('FLUTTER_ROOT').first.text, flutterRoot);
     expect(props.findAllElements('TRACK_WIDGET_CREATION').first.text, 'true');
     expect(props.findAllElements('TREE_SHAKE_ICONS').first.text, 'true');
-    expect(props.findAllElements('EXTRA_GEN_SNAPSHOT_OPTIONS').first.text, '--enable-experiment=non-nullable');
-    expect(props.findAllElements('EXTRA_FRONT_END_OPTIONS').first.text, '--enable-experiment=non-nullable');
+    expect(props.findAllElements('EXTRA_GEN_SNAPSHOT_OPTIONS').first.text, '--enable-experiment%3Dnon-nullable');
+    expect(props.findAllElements('EXTRA_FRONT_END_OPTIONS').first.text, '--enable-experiment%3Dnon-nullable');
     expect(props.findAllElements('DART_DEFINES').first.text, 'foo%3Da,bar%3Db');
     expect(props.findAllElements('DART_OBFUSCATION').first.text, 'true');
     expect(props.findAllElements('SPLIT_DEBUG_INFO').first.text, r'C:\foo\');
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
index 3a4972b806..7d4b64360b 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
@@ -184,7 +184,7 @@ class FakeFlutterTestRunner implements FlutterTestRunner {
     Directory coverageDirectory,
     bool web = false,
     String randomSeed,
-    @override List<String> dartExperiments,
+    @override List<String> extraFrontEndOptions,
   }) async {
     lastEnableObservatoryValue = enableObservatory;
     return exitCode;
diff --git a/packages/flutter_tools/test/general.shard/build_info_test.dart b/packages/flutter_tools/test/general.shard/build_info_test.dart
index 98bce84ae6..1dec879e6a 100644
--- a/packages/flutter_tools/test/general.shard/build_info_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_info_test.dart
@@ -113,8 +113,8 @@ void main() {
       'DART_DEFINES': 'foo%3D2,bar%3D2',
       'DART_OBFUSCATION': 'true',
       'SPLIT_DEBUG_INFO': 'foo/',
-      'EXTRA_FRONT_END_OPTIONS': '--enable-experiment=non-nullable,bar',
-      'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment=non-nullable,fizz',
+      'EXTRA_FRONT_END_OPTIONS': '--enable-experiment%3Dnon-nullable,bar',
+      'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment%3Dnon-nullable,fizz',
     });
   });
 
@@ -129,18 +129,18 @@ void main() {
   testWithoutContext('decodeDartDefines decodes URI encoded dart defines', () {
     expect(decodeDartDefines(<String, String>{
       kDartDefines: '%22hello%22'
-    }), <String>['"hello"']);
+    }, kDartDefines), <String>['"hello"']);
     expect(decodeDartDefines(<String, String>{
       kDartDefines: 'https%3A%2F%2Fwww.google.com'
-    }), <String>['https://www.google.com']);
+    }, kDartDefines), <String>['https://www.google.com']);
     expect(decodeDartDefines(<String, String>{
       kDartDefines: '2%2C3%2C4,5'
-    }), <String>['2,3,4', '5']);
+    }, kDartDefines), <String>['2,3,4', '5']);
     expect(decodeDartDefines(<String, String>{
       kDartDefines: 'true,false,flase'
-    }), <String>['true', 'false', 'flase']);
+    }, kDartDefines), <String>['true', 'false', 'flase']);
     expect(decodeDartDefines(<String, String>{
       kDartDefines: '1232%2C456,2'
-    }), <String>['1232,456', '2']);
+    }, kDartDefines), <String>['1232,456', '2']);
   });
 }
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
index 91e9e17f47..cbe90a51ca 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
@@ -255,7 +255,7 @@ void main() {
 
   test('Dart2JSTarget calls dart2js with expected args with enabled experiment', () => testbed.run(() async {
     environment.defines[kBuildMode] = 'profile';
-    environment.defines[kEnableExperiment] = 'non-nullable';
+    environment.defines[kExtraFrontEndOptions] = '--enable-experiment=non-nullable';
     processManager.addCommand(FakeCommand(
       command: <String>[
         ...kDart2jsLinuxArgs,
diff --git a/packages/flutter_tools/test/general.shard/flutter_platform_test.dart b/packages/flutter_tools/test/general.shard/flutter_platform_test.dart
index c314cdf700..827ba061da 100644
--- a/packages/flutter_tools/test/general.shard/flutter_platform_test.dart
+++ b/packages/flutter_tools/test/general.shard/flutter_platform_test.dart
@@ -22,7 +22,7 @@ void main() {
         buildMode: BuildMode.debug,
         shellPath: '/',
         explicitObservatoryPort: 1234,
-        dartExperiments: <String>[],
+        extraFrontEndOptions: <String>[],
       );
       flutterPlatform.loadChannel('test1.dart', MockSuitePlatform());
 
@@ -35,7 +35,7 @@ void main() {
         buildMode: BuildMode.debug,
         shellPath: '/',
         precompiledDillPath: 'example.dill',
-        dartExperiments: <String>[],
+        extraFrontEndOptions: <String>[],
       );
       flutterPlatform.loadChannel('test1.dart', MockSuitePlatform());
 
@@ -113,7 +113,7 @@ void main() {
         shellPath: 'abc',
         enableObservatory: false,
         startPaused: true,
-        dartExperiments: <String>[],
+        extraFrontEndOptions: <String>[],
       ), throwsAssertionError);
 
       expect(() => installHook(
@@ -122,7 +122,7 @@ void main() {
         enableObservatory: false,
         startPaused: false,
         observatoryPort: 123,
-        dartExperiments: <String>[],
+        extraFrontEndOptions: <String>[],
       ), throwsAssertionError);
 
       FlutterPlatform capturedPlatform;
@@ -143,7 +143,7 @@ void main() {
         observatoryPort: 200,
         serverType: InternetAddressType.IPv6,
         icudtlPath: 'ghi',
-        dartExperiments: <String>[],
+        extraFrontEndOptions: <String>[],
         platformPluginRegistration: (FlutterPlatform platform) {
           capturedPlatform = platform;
         });
@@ -192,7 +192,7 @@ class TestFlutterPlatform extends FlutterPlatform {
     startPaused: false,
     enableObservatory: false,
     buildTestAssets: false,
-    dartExperiments: <String>[],
+    extraFrontEndOptions: <String>[],
   );
 
   @override
-- 
2.21.0