[flutter_releases] Flutter 1.26.0-17.2.pre Beta Framework Cherrypicks (#75303)

* Only show devtools deep links for render overflow errors (#74916)

* Add fixes for nullOk changes (#74866)

* [flutter_tools] delete old directories when unzipping ontop of them (#74818)

Fixes #74772

stale files from previous SDKs were getting left in the cache, confusing the analyzer.

* Make flutter create --smaple null-safe (#74844)

* Suppress 'Info: Compiling without sound null safety' message when building flutter_tools (#74740)

The CFE now logs to stdout by default when compiling a program which has
non-null-safe dependencies. Since flutter_tools has not yet migrated, we
need to suppress this message when compiling the tool.

Fixes https://github.com/flutter/flutter/issues/74366

* Split tools_tests subshards into separate shards to support sub-sub-sharding (#75033)

* Adopt tool_test shard builders (#75171)

* Adopt subshard naming convention in build_tests (#75179)

* Mark tool_integration_tests not flaky (#75273)

* roll flutter/plugins to ToT to fix analysis error

* update engine hash

Co-authored-by: Kenzie Schmoll <43759233+kenzieschmoll@users.noreply.github.com>
Co-authored-by: Kate Lovett <katelovett@google.com>
Co-authored-by: Jonah Williams <jonahwilliams@google.com>
Co-authored-by: Michael Goderbauer <goderbauer@google.com>
Co-authored-by: Ben Konyi <bkonyi@google.com>
Co-authored-by: Jenn Magder <magder@google.com>
diff --git a/bin/internal/engine.version b/bin/internal/engine.version
index 75251f5..ccceb04 100644
--- a/bin/internal/engine.version
+++ b/bin/internal/engine.version
@@ -1 +1 @@
-5d3477eab415fbcc3d58624b07f0a768f8208f98
+2c527d6c7e70e2f51bca1a46f1174b250f84c5da
diff --git a/bin/internal/flutter_plugins.version b/bin/internal/flutter_plugins.version
index d3b9e2e..82ff2d4 100644
--- a/bin/internal/flutter_plugins.version
+++ b/bin/internal/flutter_plugins.version
@@ -1 +1 @@
-919eaef4dcc7b3e272850b74e9995a87d7b5ab39
+9e1d573e1e953ddc2e4c7c823f442244f1ff4ebf
diff --git a/bin/internal/shared.bat b/bin/internal/shared.bat
index d5ddc20..24e7fd0 100644
--- a/bin/internal/shared.bat
+++ b/bin/internal/shared.bat
@@ -156,9 +156,9 @@
     POPD
 
     IF "%FLUTTER_TOOL_ARGS%" == "" (
-      "%dart%" --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" --no-enable-mirrors "%script_path%"
+      "%dart%" --verbosity=error --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" --no-enable-mirrors "%script_path%"
     ) else (
-      "%dart%" "%FLUTTER_TOOL_ARGS%" --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" "%script_path%"
+      "%dart%" "%FLUTTER_TOOL_ARGS%" --verbosity=error --snapshot="%snapshot_path%" --packages="%flutter_tools_dir%\.packages" "%script_path%"
     )
     IF "%ERRORLEVEL%" NEQ "0" (
       ECHO Error: Unable to create dart snapshot for flutter tool. 1>&2
diff --git a/bin/internal/shared.sh b/bin/internal/shared.sh
index de2ca90..c44f867 100644
--- a/bin/internal/shared.sh
+++ b/bin/internal/shared.sh
@@ -151,7 +151,7 @@
 
     retry_upgrade
 
-    "$DART" --disable-dart-dev $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" --no-enable-mirrors "$SCRIPT_PATH"
+    "$DART" --verbosity=error --disable-dart-dev $FLUTTER_TOOL_ARGS --snapshot="$SNAPSHOT_PATH" --packages="$FLUTTER_TOOLS_DIR/.packages" --no-enable-mirrors "$SCRIPT_PATH"
     echo "$revision" > "$STAMP_PATH"
   fi
   # The exit here is extraneous since the function is run in a subshell, but
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index b92421c..fecc3d2 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -62,6 +62,9 @@
 /// and make sure it runs _all_ shards.
 const int kBuildTestShardCount = 2;
 
+const String kShardKey = 'SHARD';
+const String kSubshardKey = 'SUBSHARD';
+
 /// The number of Cirrus jobs that run Web tests in parallel.
 ///
 /// The default is 8 shards. Typically .cirrus.yml would define the
@@ -88,6 +91,8 @@
   'test/examples/sector_layout_test.dart',
 ];
 
+const String kSmokeTestShardName = 'smoke_tests';
+
 /// When you call this, you can pass additional arguments to pass custom
 /// arguments to flutter test. For example, you might want to call this
 /// script with the parameter --local-engine=host_debug_unopt to
@@ -107,18 +112,20 @@
     print('═' * 80);
     await _runSmokeTests();
     print('═' * 80);
-    await selectShard(const <String, ShardRunner>{
+    await selectShard(<String, ShardRunner>{
       'add_to_app_life_cycle_tests': _runAddToAppLifeCycleTests,
       'build_tests': _runBuildTests,
       'framework_coverage': _runFrameworkCoverage,
       'framework_tests': _runFrameworkTests,
       'tool_coverage': _runToolCoverage,
       'tool_tests': _runToolTests,
+      'tool_integration_tests': _runIntegrationToolTests,
       'web_tool_tests': _runWebToolTests,
       'web_tests': _runWebUnitTests,
       'web_integration_tests': _runWebIntegrationTests,
       'web_long_running_tests': _runWebLongRunningTests,
       'flutter_plugins': _runFlutterPluginsTests,
+      kSmokeTestShardName: () async {}, // No-op, the smoke tests already ran. Used for testing this script.
     });
   } on ExitException catch (error) {
     error.apply();
@@ -183,68 +190,96 @@
   // We run the "pass" and "fail" smoke tests first, and alone, because those
   // are particularly critical and sensitive. If one of these fails, there's no
   // point even trying the others.
-  await _runFlutterTest(automatedTests,
-    script: path.join('test_smoke_test', 'pass_test.dart'),
-    printOutput: false,
-  );
-  await _runFlutterTest(automatedTests,
-    script: path.join('test_smoke_test', 'fail_test.dart'),
-    expectFailure: true,
-    printOutput: false,
-  );
-  // We run the timeout tests individually because they are timing-sensitive.
-  await _runFlutterTest(automatedTests,
-    script: path.join('test_smoke_test', 'timeout_pass_test.dart'),
-    expectFailure: false,
-    printOutput: false,
-  );
-  await _runFlutterTest(automatedTests,
-    script: path.join('test_smoke_test', 'timeout_fail_test.dart'),
-    expectFailure: true,
-    printOutput: false,
-  );
-  await _runFlutterTest(automatedTests,
-    script: path.join('test_smoke_test', 'pending_timer_fail_test.dart'),
-    expectFailure: true,
-    printOutput: false,
-    outputChecker: (CommandResult result) {
-      return result.flattenedStdout.contains('failingPendingTimerTest')
-        ? null
-        : 'Failed to find the stack trace for the pending Timer.';
-    }
-  );
-  // We run the remaining smoketests in parallel, because they each take some
-  // time to run (e.g. compiling), so we don't want to run them in series,
-  // especially on 20-core machines...
-  await Future.wait<void>(
-    <Future<void>>[
-      _runFlutterTest(automatedTests,
-        script: path.join('test_smoke_test', 'crash1_test.dart'),
-        expectFailure: true,
-        printOutput: false,
-      ),
-      _runFlutterTest(automatedTests,
-        script: path.join('test_smoke_test', 'crash2_test.dart'),
-        expectFailure: true,
-        printOutput: false,
-      ),
-      _runFlutterTest(automatedTests,
-        script: path.join('test_smoke_test', 'syntax_error_test.broken_dart'),
-        expectFailure: true,
-        printOutput: false,
-      ),
-      _runFlutterTest(automatedTests,
-        script: path.join('test_smoke_test', 'missing_import_test.broken_dart'),
-        expectFailure: true,
-        printOutput: false,
-      ),
-      _runFlutterTest(automatedTests,
-        script: path.join('test_smoke_test', 'disallow_error_reporter_modification_test.dart'),
-        expectFailure: true,
-        printOutput: false,
-      ),
-    ],
-  );
+  final List<ShardRunner> tests = <ShardRunner>[
+    () => _runFlutterTest(
+          automatedTests,
+          script: path.join('test_smoke_test', 'pass_test.dart'),
+          printOutput: false,
+        ),
+    () => _runFlutterTest(
+          automatedTests,
+          script: path.join('test_smoke_test', 'fail_test.dart'),
+          expectFailure: true,
+          printOutput: false,
+        ),
+    // We run the timeout tests individually because they are timing-sensitive.
+    () => _runFlutterTest(
+          automatedTests,
+          script: path.join('test_smoke_test', 'timeout_pass_test.dart'),
+          expectFailure: false,
+          printOutput: false,
+        ),
+    () => _runFlutterTest(
+          automatedTests,
+          script: path.join('test_smoke_test', 'timeout_fail_test.dart'),
+          expectFailure: true,
+          printOutput: false,
+        ),
+    () => _runFlutterTest(automatedTests,
+            script:
+                path.join('test_smoke_test', 'pending_timer_fail_test.dart'),
+            expectFailure: true,
+            printOutput: false, outputChecker: (CommandResult result) {
+          return result.flattenedStdout.contains('failingPendingTimerTest')
+              ? null
+              : 'Failed to find the stack trace for the pending Timer.';
+        }),
+    // We run the remaining smoketests in parallel, because they each take some
+    // time to run (e.g. compiling), so we don't want to run them in series,
+    // especially on 20-core machines...
+    () => Future.wait<void>(
+          <Future<void>>[
+            _runFlutterTest(
+              automatedTests,
+              script: path.join('test_smoke_test', 'crash1_test.dart'),
+              expectFailure: true,
+              printOutput: false,
+            ),
+            _runFlutterTest(
+              automatedTests,
+              script: path.join('test_smoke_test', 'crash2_test.dart'),
+              expectFailure: true,
+              printOutput: false,
+            ),
+            _runFlutterTest(
+              automatedTests,
+              script:
+                  path.join('test_smoke_test', 'syntax_error_test.broken_dart'),
+              expectFailure: true,
+              printOutput: false,
+            ),
+            _runFlutterTest(
+              automatedTests,
+              script: path.join(
+                  'test_smoke_test', 'missing_import_test.broken_dart'),
+              expectFailure: true,
+              printOutput: false,
+            ),
+            _runFlutterTest(
+              automatedTests,
+              script: path.join('test_smoke_test',
+                  'disallow_error_reporter_modification_test.dart'),
+              expectFailure: true,
+              printOutput: false,
+            ),
+          ],
+        ),
+  ];
+
+  List<ShardRunner> testsToRun;
+
+  // Smoke tests are special and run first for all test shards.
+  // Run all smoke tests for other shards.
+  // Only shard smoke tests when explicitly specified.
+  final String shardName = Platform.environment[kShardKey];
+  if (shardName == kSmokeTestShardName) {
+    testsToRun = _selectIndexOfTotalSubshard<ShardRunner>(tests);
+  } else {
+    testsToRun = tests;
+  }
+  for (final ShardRunner test in testsToRun) {
+    await test();
+  }
 
   // Verify that we correctly generated the version file.
   final String versionError = await verifyVersion(File(path.join(flutterRoot, 'version')));
@@ -276,38 +311,46 @@
   );
 }
 
-Future<void> _runToolTests() async {
-  const String kDotShard = '.shard';
-  const String kWeb = 'web';
-  const String kTest = 'test';
-  final String toolsPath = path.join(flutterRoot, 'packages', 'flutter_tools');
-
-  final Map<String, ShardRunner> subshards = Map<String, ShardRunner>.fromIterable(
-    Directory(path.join(toolsPath, kTest))
-      .listSync()
-      .map<String>((FileSystemEntity entry) => entry.path)
-      .where((String name) => name.endsWith(kDotShard))
-      .where((String name) => path.basenameWithoutExtension(name) != kWeb)
-      .map<String>((String name) => path.basenameWithoutExtension(name)),
-    // The `dynamic` on the next line is because Map.fromIterable isn't generic.
-    value: (dynamic subshard) => () async {
-      // Due to https://github.com/flutter/flutter/issues/46180, skip the hermetic directory
-      // on Windows.
-      final String suffix = Platform.isWindows && subshard == 'commands'
-        ? 'permeable'
-        : '';
-      await _pubRunTest(
-        toolsPath,
-        forceSingleCore: subshard != 'general',
-        testPaths: <String>[path.join(kTest, '$subshard$kDotShard', suffix)],
-        enableFlutterToolAsserts: subshard != 'general',
-        // Detect unit test time regressions (poor time delay handling, etc).
-        perTestTimeout: (subshard == 'general') ? const Duration(seconds: 2) : null,
-      );
-    },
+Future<void> _runGeneralToolTests() async {
+  await _pubRunTest(
+    path.join(flutterRoot, 'packages', 'flutter_tools'),
+    testPaths: <String>[path.join('test', 'general.shard')],
+    enableFlutterToolAsserts: false,
+    // Detect unit test time regressions (poor time delay handling, etc).
+    perTestTimeout: const Duration(seconds: 2),
   );
+}
 
-  await selectSubshard(subshards);
+Future<void> _runCommandsToolTests() async {
+  // Due to https://github.com/flutter/flutter/issues/46180, skip the hermetic directory
+  // on Windows.
+  final String suffix = Platform.isWindows ? 'permeable' : '';
+  await _pubRunTest(
+    path.join(flutterRoot, 'packages', 'flutter_tools'),
+    forceSingleCore: true,
+    testPaths: <String>[path.join('test', 'commands.shard', suffix)],
+  );
+}
+
+Future<void> _runIntegrationToolTests() async {
+  final String toolsPath = path.join(flutterRoot, 'packages', 'flutter_tools');
+  final List<String> allTests = Directory(path.join(toolsPath, 'test', 'integration.shard'))
+      .listSync(recursive: true).whereType<File>()
+      .map<String>((FileSystemEntity entry) => path.relative(entry.path, from: toolsPath))
+      .where((String testPath) => path.basename(testPath).endsWith('_test.dart')).toList();
+
+  await _pubRunTest(
+    toolsPath,
+    forceSingleCore: true,
+    testPaths: _selectIndexOfTotalSubshard<String>(allTests),
+  );
+}
+
+Future<void> _runToolTests() async {
+  await selectSubshard(<String, ShardRunner>{
+    'general': _runGeneralToolTests,
+    'commands': _runCommandsToolTests,
+  });
 }
 
 Future<void> _runWebToolTests() async {
@@ -362,7 +405,10 @@
       ],
   ]..shuffle(math.Random(0));
 
-  await _selectIndexedSubshard(tests, kBuildTestShardCount);
+  if (!await _runShardRunnerIndexOfTotalSubshard(tests)) {
+    // TODO(jmagman): Remove fallback once LUCI configs are migrated to d+_d+ subshard format.
+    await _selectIndexedSubshard(tests, kBuildTestShardCount);
+  }
 }
 
 Future<void> _runExampleProjectBuildTests(FileSystemEntity exampleDirectory) async {
@@ -810,7 +856,10 @@
     () => _runGalleryE2eWebTest('release', canvasKit: true),
   ];
   await _ensureChromeDriverIsRunning();
-  await _selectIndexedSubshard(tests, kWebLongRunningTestShardCount);
+  if (!await _runShardRunnerIndexOfTotalSubshard(tests)) {
+    // TODO(jmagman): Remove fallback once LUCI configs are migrated to d+_d+ subshard format.
+    await _selectIndexedSubshard(tests, kWebLongRunningTestShardCount);
+  }
   await _stopChromeDriver();
 }
 
@@ -1457,6 +1506,60 @@
   await selectSubshard(subshards);
 }
 
+/// Parse (one-)index/total-named subshards from environment variable SUBSHARD
+/// and equally distribute [tests] between them.
+/// Subshard format is "{index}_{total number of shards}".
+/// The scheduler can change the number of total shards without needing an additional
+/// commit in this repository.
+///
+/// Examples:
+/// 1_3
+/// 2_3
+/// 3_3
+List<T> _selectIndexOfTotalSubshard<T>(List<T> tests, {String subshardKey = kSubshardKey}) {
+  // Example: "1_3" means the first (one-indexed) shard of three total shards.
+  final String subshardName = Platform.environment[subshardKey];
+  if (subshardName == null) {
+    print('$kSubshardKey environment variable is missing, skipping sharding');
+    return tests;
+  }
+  print('$bold$subshardKey=$subshardName$reset');
+
+  final RegExp pattern = RegExp(r'^(\d+)_(\d+)$');
+  final Match match = pattern.firstMatch(subshardName);
+  if (match == null || match.groupCount != 2) {
+    print('${red}Invalid subshard name "$subshardName". Expected format "[int]_[int]" ex. "1_3"');
+    // TODO(jmagman): exit(1) here instead once LUCI configs are migrated to d+_d+ subshard format.
+    return null;
+  }
+  // One-indexed.
+  final int index = int.parse(match.group(1));
+  final int total = int.parse(match.group(2));
+  if (index > total) {
+    print('${red}Invalid subshard name "$subshardName". Index number must be greater or equal to total.');
+    exit(1);
+  }
+
+  final int testsPerShard = tests.length ~/ total;
+  final int start = (index - 1) * testsPerShard;
+  final int end = index * testsPerShard;
+
+  print('Selecting subshard $index of $total (range ${start + 1}-$end of ${tests.length})');
+  return tests.sublist(start, end);
+}
+
+Future<bool> _runShardRunnerIndexOfTotalSubshard(List<ShardRunner> tests) async {
+  final List<ShardRunner> sublist = _selectIndexOfTotalSubshard<ShardRunner>(tests);
+  // TODO(jmagman): Remove the boolean return to indicate fallback to unsharded variant once LUCI configs are migrated to d+_d+ subshard format.
+  if (sublist == null) {
+    return false;
+  }
+  for (final ShardRunner test in sublist) {
+    await test();
+  }
+  return true;
+}
+
 /// If the CIRRUS_TASK_NAME environment variable exists, we use that to determine
 /// the shard and sub-shard (parsing it in the form shard-subshard-platform, ignoring
 /// the platform).
@@ -1465,8 +1568,8 @@
 /// environment variables. For example, to run all the framework tests you can
 /// just set SHARD=framework_tests. To run specifically the third subshard of
 /// the Web tests you can set SHARD=web_tests SUBSHARD=2 (it's zero-based).
-Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, 'SHARD', 'shard', 0);
-Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard', 1);
+Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, kShardKey, 'shard', 0);
+Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, kSubshardKey, 'subshard', 1);
 
 const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
 
diff --git a/dev/bots/test/test_test.dart b/dev/bots/test/test_test.dart
index fb65219..d2e168d 100644
--- a/dev/bots/test/test_test.dart
+++ b/dev/bots/test/test_test.dart
@@ -8,6 +8,7 @@
 import 'package:file/memory.dart';
 import 'package:mockito/mockito.dart';
 import 'package:path/path.dart' as path;
+import 'package:process/process.dart';
 
 import '../test.dart';
 import 'common.dart';
@@ -80,4 +81,44 @@
       expect(actualHash, kSampleHash);
     });
   });
+
+  group('test.dart script', () {
+    const ProcessManager processManager = LocalProcessManager();
+
+    Future<ProcessResult> runScript(
+        [Map<String, String> environment, List<String> otherArgs = const <String>[]]) async {
+      final String dart = path.absolute(
+          path.join('..', '..', 'bin', 'cache', 'dart-sdk', 'bin', 'dart'));
+      final ProcessResult scriptProcess = processManager.runSync(<String>[
+        dart,
+        'test.dart',
+        ...otherArgs,
+      ], environment: environment);
+      return scriptProcess;
+    }
+
+    test('subshards tests correctly', () async {
+      ProcessResult result = await runScript(
+        <String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '1_3'},
+      );
+      expect(result.exitCode, 0);
+      // There are currently 6 smoke tests. This shard should contain test 1 and 2.
+      expect(result.stdout, contains('Selecting subshard 1 of 3 (range 1-2 of 6)'));
+
+      result = await runScript(
+        <String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '5_6'},
+      );
+      expect(result.exitCode, 0);
+      // This shard should contain only test 5.
+      expect(result.stdout, contains('Selecting subshard 5 of 6 (range 5-5 of 6)'));
+    });
+
+    test('exits with code 1 when SUBSHARD index greater than total', () async {
+      final ProcessResult result = await runScript(
+        <String, String>{'SHARD': 'smoke_tests', 'SUBSHARD': '100_99'},
+      );
+      expect(result.exitCode, 1);
+      expect(result.stdout, contains('Invalid subshard name'));
+    });
+  });
 }
diff --git a/dev/prod_builders.json b/dev/prod_builders.json
index 84020cd..1f005ee 100644
--- a/dev/prod_builders.json
+++ b/dev/prod_builders.json
@@ -379,6 +379,12 @@
          "flaky": false
       },
       {
+         "name": "Linux tool_integration_tests",
+         "repo": "flutter",
+         "task_name": "linux_tool_integration_tests",
+         "flaky": false
+      },
+      {
          "name": "Linux web_tool_tests",
          "repo": "flutter",
          "task_name": "linux_web_tool_tests",
@@ -763,6 +769,12 @@
          "flaky": false
       },
       {
+         "name": "Mac tool_integration_tests",
+         "repo": "flutter",
+         "task_name": "mac_tool_integration_tests",
+         "flaky": false
+      },
+      {
          "name": "Windows build_aar_module_test",
          "repo": "flutter",
          "task_name": "win_build_aar_module_test",
@@ -841,6 +853,12 @@
          "flaky": false
       },
       {
+         "name": "Windows tool_integration_tests",
+         "repo": "flutter",
+         "task_name": "win_tool_integration_tests",
+         "flaky": false
+      },
+      {
          "name": "Windows web_tool_tests",
          "repo": "flutter",
          "task_name": "win_web_tool_tests",
diff --git a/dev/try_builders.json b/dev/try_builders.json
index 56ad3a9..b3f6193 100644
--- a/dev/try_builders.json
+++ b/dev/try_builders.json
@@ -134,6 +134,13 @@
          "run_if":["dev/", "packages/flutter_tools/", "bin/"]
       },
       {
+         "name":"Linux tool_integration_tests",
+         "repo":"flutter",
+         "task_name":"linux_tool_integration_tests",
+         "enabled":true,
+         "run_if":["dev/", "packages/flutter_tools/", "bin/"]
+      },
+      {
          "name":"Linux web_tool_tests",
          "repo":"flutter",
          "task_name":"linux_web_tool_tests",
@@ -299,6 +306,13 @@
          "run_if":["dev/**", "packages/flutter_tools/**", "bin/**"]
       },
       {
+         "name":"Mac tool_integration_tests",
+         "repo":"flutter",
+         "task_name":"mac_tool_integration_tests",
+         "enabled":true,
+         "run_if":["dev/**", "packages/flutter_tools/**", "bin/**"]
+      },
+      {
          "name":"Mac web_tool_tests",
          "repo":"flutter",
          "task_name":"mac_web_tool_tests",
@@ -384,7 +398,15 @@
         "name": "Windows tool_tests",
         "repo": "flutter",
         "task_name": "win_tool_tests",
-        "enabled":true
+        "enabled":true,
+        "run_if":["dev/**", "packages/flutter_tools/**", "bin/**"]
+      },
+      {
+         "name": "Windows tool_integration_tests",
+         "repo": "flutter",
+         "task_name": "win_tool_integration_tests",
+         "enabled":true,
+         "run_if":["dev/**", "packages/flutter_tools/**", "bin/**"]
       },
       {
          "name": "Windows web_tool_tests",
diff --git a/packages/flutter/lib/fix_data.yaml b/packages/flutter/lib/fix_data.yaml
index e05be77..3c65216 100644
--- a/packages/flutter/lib/fix_data.yaml
+++ b/packages/flutter/lib/fix_data.yaml
@@ -11,6 +11,463 @@
 version: 1
 transforms:
 
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart' ]
+      method: 'resolveFrom'
+      inClass: 'MaterialBasedCupertinoThemeData'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'resolveFrom'
+      inClass: 'CupertinoTextThemeData'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'resolveFrom'
+      inClass: 'NoDefaultCupertinoThemeData'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'resolveFrom'
+      inClass: 'CupertinoThemeData'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68736.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'brightnessOf'
+      inClass: 'CupertinoTheme'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeBrightnessOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'CupertinoUserInterfaceLevel'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'resolveFrom'
+      inClass: 'CupertinoDynamicColor'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68905.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'cupertino.dart' ]
+      method: 'resolve'
+      inClass: 'CupertinoDynamicColor'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeResolve'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68925.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'SliverAnimatedList'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68925.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'AnimatedList'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68921.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'invoke'
+      inClass: 'Actions'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeInvoke'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68921.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'find'
+      inClass: 'Actions'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeFind'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68921.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'handler'
+      inClass: 'Actions'
+    changes:
+      - kind: 'removeParameter'
+        name: 'nullOk'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68921.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'Shortcuts'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68917.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'Focus'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68917.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'FocusTraversalGroup'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68917.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'FocusTraversalOrder'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68911.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'localeOf'
+      inClass: 'Localizations'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeLocaleOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68910.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'Router'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68908.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart' ]
+      method: 'of'
+      inClass: 'Scaffold'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68908.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart' ]
+      method: 'of'
+      inClass: 'ScaffoldMessenger'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/70726.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'Navigator'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
+  # Changes made in https://github.com/flutter/flutter/pull/68736.
+  - title: "Migrate from 'nullOk'"
+    date: 2021-01-27
+    element:
+      uris: [ 'material.dart', 'widgets.dart', 'cupertino.dart' ]
+      method: 'of'
+      inClass: 'MediaQuery'
+    oneOf:
+      - if: "nullOk == 'true'"
+        changes:
+          - kind: 'rename'
+            newName: 'maybeOf'
+          - kind: 'removeParameter'
+            name: 'nullOk'
+      - if: "nullOk == 'false'"
+        changes:
+          - kind: 'removeParameter'
+            name: 'nullOk'
+    variables:
+      nullOk:
+        kind: 'fragment'
+        value: 'arguments[nullOk]'
+
   # Changes made in https://github.com/flutter/flutter/pull/44189.
   - title: 'Rename to dependOnInheritedElement'
     date: 2020-12-23
diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart
index 63bfe0f..dffeab6 100644
--- a/packages/flutter/lib/src/widgets/widget_inspector.dart
+++ b/packages/flutter/lib/src/widgets/widget_inspector.dart
@@ -2873,12 +2873,19 @@
 /// in [WidgetsBinding.initInstances].
 Iterable<DiagnosticsNode> transformDebugCreator(Iterable<DiagnosticsNode> properties) sync* {
   final List<DiagnosticsNode> pending = <DiagnosticsNode>[];
+  ErrorSummary? errorSummary;
+  for (final DiagnosticsNode node in properties) {
+    if (node is ErrorSummary) {
+      errorSummary = node;
+      break;
+    }
+  }
   bool foundStackTrace = false;
   for (final DiagnosticsNode node in properties) {
     if (!foundStackTrace && node is DiagnosticsStackTrace)
       foundStackTrace = true;
     if (_isDebugCreator(node)) {
-      yield* _parseDiagnosticsNode(node)!;
+      yield* _parseDiagnosticsNode(node, errorSummary)!;
     } else {
       if (foundStackTrace) {
         pending.add(node);
@@ -2893,15 +2900,21 @@
 /// Transform the input [DiagnosticsNode].
 ///
 /// Return null if input [DiagnosticsNode] is not applicable.
-Iterable<DiagnosticsNode>? _parseDiagnosticsNode(DiagnosticsNode node) {
+Iterable<DiagnosticsNode>? _parseDiagnosticsNode(
+  DiagnosticsNode node,
+  ErrorSummary? errorSummary,
+) {
   if (!_isDebugCreator(node))
     return null;
   final DebugCreator debugCreator = node.value! as DebugCreator;
   final Element element = debugCreator.element;
-  return _describeRelevantUserCode(element);
+  return _describeRelevantUserCode(element, errorSummary);
 }
 
-Iterable<DiagnosticsNode> _describeRelevantUserCode(Element element) {
+Iterable<DiagnosticsNode> _describeRelevantUserCode(
+  Element element,
+  ErrorSummary? errorSummary,
+) {
   if (!WidgetInspectorService.instance.isWidgetCreationTracked()) {
     return <DiagnosticsNode>[
       ErrorDescription(
@@ -2912,20 +2925,38 @@
       ErrorSpacer(),
     ];
   }
+
+  bool isOverflowError() {
+    if (errorSummary != null && errorSummary.value.isNotEmpty) {
+      final Object summary = errorSummary.value.first;
+      if (summary is String && summary.startsWith('A RenderFlex overflowed by')) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   final List<DiagnosticsNode> nodes = <DiagnosticsNode>[];
   bool processElement(Element target) {
     // TODO(chunhtai): should print out all the widgets that are about to cross
     // package boundaries.
     if (debugIsLocalCreationLocation(target)) {
 
+
       DiagnosticsNode? devToolsDiagnostic;
-      final String? devToolsInspectorUri =
-          WidgetInspectorService.instance._devToolsInspectorUriForElement(target);
-      if (devToolsInspectorUri != null) {
-        devToolsDiagnostic = DevToolsDeepLinkProperty(
-          'To inspect this widget in Flutter DevTools, visit: $devToolsInspectorUri',
-          devToolsInspectorUri,
-        );
+
+      // TODO(kenz): once the inspector is better at dealing with broken trees,
+      // we can enable deep links for more errors than just RenderFlex overflow
+      // errors. See https://github.com/flutter/flutter/issues/74918.
+      if (isOverflowError()) {
+        final String? devToolsInspectorUri =
+        WidgetInspectorService.instance._devToolsInspectorUriForElement(target);
+        if (devToolsInspectorUri != null) {
+          devToolsDiagnostic = DevToolsDeepLinkProperty(
+            'To inspect this widget in Flutter DevTools, visit: $devToolsInspectorUri',
+            devToolsInspectorUri,
+          );
+        }
       }
 
       nodes.addAll(<DiagnosticsNode>[
diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart
index 50de011..d8fe895 100644
--- a/packages/flutter/test/widgets/widget_inspector_test.dart
+++ b/packages/flutter/test/widgets/widget_inspector_test.dart
@@ -1044,6 +1044,101 @@
       expect(nodes[4].runtimeType, DiagnosticsStackTrace);
     }, skip: WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --no-track-widget-creation flag.
 
+    testWidgets('test transformDebugCreator will add DevToolsDeepLinkProperty for overflow errors', (WidgetTester tester) async {
+      activeDevToolsServerAddress = 'http://127.0.0.1:9100';
+      connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/';
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: Stack(
+            children: const <Widget>[
+              Text('a'),
+              Text('b', textDirection: TextDirection.ltr),
+              Text('c', textDirection: TextDirection.ltr),
+            ],
+          ),
+        ),
+      );
+      final Element elementA = find.text('a').evaluate().first;
+
+      final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
+      builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom'));
+      builder.add(DiagnosticsDebugCreator(DebugCreator(elementA)));
+      builder.add(StringProperty('dummy2', 'value'));
+
+      final List<DiagnosticsNode> nodes = List<DiagnosticsNode>.from(transformDebugCreator(builder.properties));
+      expect(nodes.length, 6);
+      expect(nodes[0].runtimeType, ErrorSummary);
+      expect(nodes[1].runtimeType, DiagnosticsBlock);
+      expect(nodes[2].runtimeType, ErrorSpacer);
+      expect(nodes[3].runtimeType, DevToolsDeepLinkProperty);
+      expect(nodes[4].runtimeType, ErrorSpacer);
+      expect(nodes[5].runtimeType, StringProperty);
+    }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked());
+
+    testWidgets('test transformDebugCreator will not add DevToolsDeepLinkProperty for non-overflow errors', (WidgetTester tester) async {
+      activeDevToolsServerAddress = 'http://127.0.0.1:9100';
+      connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/';
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: Stack(
+            children: const <Widget>[
+              Text('a'),
+              Text('b', textDirection: TextDirection.ltr),
+              Text('c', textDirection: TextDirection.ltr),
+            ],
+          ),
+        ),
+      );
+      final Element elementA = find.text('a').evaluate().first;
+
+      final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
+      builder.add(ErrorSummary('some other error'));
+      builder.add(DiagnosticsDebugCreator(DebugCreator(elementA)));
+      builder.add(StringProperty('dummy2', 'value'));
+
+      final List<DiagnosticsNode> nodes = List<DiagnosticsNode>.from(transformDebugCreator(builder.properties));
+      expect(nodes.length, 4);
+      expect(nodes[0].runtimeType, ErrorSummary);
+      expect(nodes[1].runtimeType, DiagnosticsBlock);
+      expect(nodes[2].runtimeType, ErrorSpacer);
+      expect(nodes[3].runtimeType, StringProperty);
+    }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked());
+
+    testWidgets('test transformDebugCreator will not add DevToolsDeepLinkProperty if devtoolsServerAddress is unavailable', (WidgetTester tester) async {
+      activeDevToolsServerAddress = null;
+      connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/';
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: Stack(
+            children: const <Widget>[
+              Text('a'),
+              Text('b', textDirection: TextDirection.ltr),
+              Text('c', textDirection: TextDirection.ltr),
+            ],
+          ),
+        ),
+      );
+      final Element elementA = find.text('a').evaluate().first;
+
+      final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
+      builder.add(ErrorSummary('A RenderFlex overflowed by 273 pixels on the bottom'));
+      builder.add(DiagnosticsDebugCreator(DebugCreator(elementA)));
+      builder.add(StringProperty('dummy2', 'value'));
+
+      final List<DiagnosticsNode> nodes = List<DiagnosticsNode>.from(transformDebugCreator(builder.properties));
+      expect(nodes.length, 4);
+      expect(nodes[0].runtimeType, ErrorSummary);
+      expect(nodes[1].runtimeType, DiagnosticsBlock);
+      expect(nodes[2].runtimeType, ErrorSpacer);
+      expect(nodes[3].runtimeType, StringProperty);
+    }, skip: !WidgetInspectorService.instance.isWidgetCreationTracked());
+
     testWidgets('WidgetInspectorService setPubRootDirectories', (WidgetTester tester) async {
       await tester.pumpWidget(
         Directionality(
diff --git a/packages/flutter/test_fixes/cupertino.dart b/packages/flutter/test_fixes/cupertino.dart
index c960b37..fac8bfe 100644
--- a/packages/flutter/test_fixes/cupertino.dart
+++ b/packages/flutter/test_fixes/cupertino.dart
@@ -5,6 +5,9 @@
 import 'package:flutter/cupertino.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
+
   // Change made in https://github.com/flutter/flutter/pull/41859
   const CupertinoTextThemeData themeData = CupertinoTextThemeData(brightness: Brightness.dark);
   themeData.copyWith(brightness: Brightness.light);
@@ -52,4 +55,64 @@
   const FormField formField = FormField(autovalidate: true);
   const FormField formField = FormField(autovalidate: false);
   final autoMode = formField.autovalidate;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.of(context, nullOk: true);
+  MediaQuery.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.of(context, nullOk: true);
+  Navigator.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.of(context, nullOk: true);
+  Router.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.localeOf(context, nullOk: true);
+  Localizations.localeOf(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.of(context, nullOk: true);
+  FocusTraversalOrder.of(context, nullOk: false);
+  FocusTraversalGroup.of(context, nullOk: true);
+  FocusTraversalGroup.of(context, nullOk: false);
+  Focus.of(context, nullOk: true);
+  Focus.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.of(context, nullOk: true);
+  Shortcuts.of(context, nullOk: false);
+  Actions.find(context, nullOk: true);
+  Actions.find(context, nullOk: false);
+  Actions.handler(context, nullOk: true);
+  Actions.handler(context, nullOk: false);
+  Actions.invoke(context, nullOk: true);
+  Actions.invoke(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.of(context, nullOk: true);
+  AnimatedList.of(context, nullOk: false);
+  SliverAnimatedList.of(context, nullOk: true);
+  SliverAnimatedList.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  CupertinoDynamicColor.resolve(Color(0), context, nullOk: true);
+  CupertinoDynamicColor.resolve(Color(0), context, nullOk: false);
+  CupertinoDynamicColor.resolveFrom(context, nullOk: true);
+  CupertinoDynamicColor.resolveFrom(context, nullOk: false);
+  CupertinoUserInterfaceLevel.of(context, nullOk: true);
+  CupertinoUserInterfaceLevel.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  CupertinoTheme.brightnessOf(context, nullOk: true);
+  CupertinoTheme.brightnessOf(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  CupertinoThemeData.resolveFrom(context, nullOk: true);
+  CupertinoThemeData.resolveFrom(context, nullOk: false);
+  NoDefaultCupertinoThemeData.resolveFrom(context, nullOk: true);
+  NoDefaultCupertinoThemeData.resolveFrom(context, nullOk: false);
+  CupertinoTextThemeData.resolveFrom(context, nullOk: true);
+  CupertinoTextThemeData.resolveFrom(context, nullOk: false);
 }
diff --git a/packages/flutter/test_fixes/cupertino.dart.expect b/packages/flutter/test_fixes/cupertino.dart.expect
index 05d04c1..f5687f6 100644
--- a/packages/flutter/test_fixes/cupertino.dart.expect
+++ b/packages/flutter/test_fixes/cupertino.dart.expect
@@ -5,6 +5,9 @@
 import 'package:flutter/cupertino.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
+
   // Change made in https://github.com/flutter/flutter/pull/41859
   const CupertinoTextThemeData themeData = CupertinoTextThemeData();
   themeData.copyWith();
@@ -52,4 +55,64 @@
   const FormField formField = FormField(autovalidateMode: AutoValidateMode.always);
   const FormField formField = FormField(autovalidateMode: AutoValidateMode.disabled);
   final autoMode = formField.autovalidateMode;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.maybeOf(context);
+  MediaQuery.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.maybeOf(context);
+  Navigator.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.maybeOf(context);
+  Router.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.maybeLocaleOf(context);
+  Localizations.localeOf(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.maybeOf(context);
+  FocusTraversalOrder.of(context);
+  FocusTraversalGroup.maybeOf(context);
+  FocusTraversalGroup.of(context);
+  Focus.maybeOf(context);
+  Focus.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.maybeOf(context);
+  Shortcuts.of(context);
+  Actions.maybeFind(context);
+  Actions.find(context);
+  Actions.handler(context);
+  Actions.handler(context);
+  Actions.maybeInvoke(context);
+  Actions.invoke(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.maybeOf(context);
+  AnimatedList.of(context);
+  SliverAnimatedList.maybeOf(context);
+  SliverAnimatedList.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  CupertinoDynamicColor.maybeResolve(Color(0), context);
+  CupertinoDynamicColor.resolve(Color(0), context);
+  CupertinoDynamicColor.resolveFrom(context);
+  CupertinoDynamicColor.resolveFrom(context);
+  CupertinoUserInterfaceLevel.maybeOf(context);
+  CupertinoUserInterfaceLevel.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  CupertinoTheme.maybeBrightnessOf(context);
+  CupertinoTheme.brightnessOf(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  CupertinoThemeData.resolveFrom(context);
+  CupertinoThemeData.resolveFrom(context);
+  NoDefaultCupertinoThemeData.resolveFrom(context);
+  NoDefaultCupertinoThemeData.resolveFrom(context);
+  CupertinoTextThemeData.resolveFrom(context);
+  CupertinoTextThemeData.resolveFrom(context);
 }
diff --git a/packages/flutter/test_fixes/material.dart b/packages/flutter/test_fixes/material.dart
index 9d161ad..69e9142 100644
--- a/packages/flutter/test_fixes/material.dart
+++ b/packages/flutter/test_fixes/material.dart
@@ -5,6 +5,8 @@
 import 'package:flutter/material.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
 
   // Changes made in https://github.com/flutter/flutter/pull/26259
   const Scaffold scaffold = Scaffold(resizeToAvoidBottomPadding: true);
@@ -114,4 +116,54 @@
   style = textTheme.button;
   style = textTheme.subtitle;
   style = textTheme.overline;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.of(context, nullOk: true);
+  MediaQuery.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.of(context, nullOk: true);
+  Navigator.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68908
+  ScaffoldMessenger.of(context, nullOk: true);
+  ScaffoldMessenger.of(context, nullOk: false);
+  Scaffold.of(context, nullOk: true);
+  Scaffold.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.of(context, nullOk: true);
+  Router.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.localeOf(context, nullOk: true);
+  Localizations.localeOf(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.of(context, nullOk: true);
+  FocusTraversalOrder.of(context, nullOk: false);
+  FocusTraversalGroup.of(context, nullOk: true);
+  FocusTraversalGroup.of(context, nullOk: false);
+  Focus.of(context, nullOk: true);
+  Focus.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.of(context, nullOk: true);
+  Shortcuts.of(context, nullOk: false);
+  Actions.find(context, nullOk: true);
+  Actions.find(context, nullOk: false);
+  Actions.handler(context, nullOk: true);
+  Actions.handler(context, nullOk: false);
+  Actions.invoke(context, nullOk: true);
+  Actions.invoke(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.of(context, nullOk: true);
+  AnimatedList.of(context, nullOk: false);
+  SliverAnimatedList.of(context, nullOk: true);
+  SliverAnimatedList.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  MaterialBasedCupertinoThemeData.resolveFrom(context, nullOk: true);
+  MaterialBasedCupertinoThemeData.resolveFrom(context, nullOk: false);
 }
diff --git a/packages/flutter/test_fixes/material.dart.expect b/packages/flutter/test_fixes/material.dart.expect
index 03914b2..42f430c 100644
--- a/packages/flutter/test_fixes/material.dart.expect
+++ b/packages/flutter/test_fixes/material.dart.expect
@@ -5,6 +5,8 @@
 import 'package:flutter/material.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
 
   // Changes made in https://github.com/flutter/flutter/pull/26259
   const Scaffold scaffold = Scaffold(resizeToAvoidBottomInset: true);
@@ -114,4 +116,54 @@
   style = textTheme.button;
   style = textTheme.subtitle2;
   style = textTheme.overline;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.maybeOf(context);
+  MediaQuery.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.maybeOf(context);
+  Navigator.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68908
+  ScaffoldMessenger.maybeOf(context);
+  ScaffoldMessenger.of(context);
+  Scaffold.maybeOf(context);
+  Scaffold.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.maybeOf(context);
+  Router.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.maybeLocaleOf(context);
+  Localizations.localeOf(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.maybeOf(context);
+  FocusTraversalOrder.of(context);
+  FocusTraversalGroup.maybeOf(context);
+  FocusTraversalGroup.of(context);
+  Focus.maybeOf(context);
+  Focus.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.maybeOf(context);
+  Shortcuts.of(context);
+  Actions.maybeFind(context);
+  Actions.find(context);
+  Actions.handler(context);
+  Actions.handler(context);
+  Actions.maybeInvoke(context);
+  Actions.invoke(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.maybeOf(context);
+  AnimatedList.of(context);
+  SliverAnimatedList.maybeOf(context);
+  SliverAnimatedList.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68905
+  MaterialBasedCupertinoThemeData.resolveFrom(context);
+  MaterialBasedCupertinoThemeData.resolveFrom(context);
 }
diff --git a/packages/flutter/test_fixes/widgets.dart b/packages/flutter/test_fixes/widgets.dart
index 0de42a0..c882ee8 100644
--- a/packages/flutter/test_fixes/widgets.dart
+++ b/packages/flutter/test_fixes/widgets.dart
@@ -5,6 +5,9 @@
 import 'package:flutter/widgets.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
+
   // Changes made in https://github.com/flutter/flutter/pull/44189
   const Element element = Element(myWidget);
   element.inheritFromElement(ancestor);
@@ -48,4 +51,44 @@
   const Stack stack = Stack(overflow: Overflow.visible);
   const Stack stack = Stack(overflow: Overflow.clip);
   final behavior = stack.overflow;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.of(context, nullOk: true);
+  MediaQuery.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.of(context, nullOk: true);
+  Navigator.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.of(context, nullOk: true);
+  Router.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.localeOf(context, nullOk: true);
+  Localizations.localeOf(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.of(context, nullOk: true);
+  FocusTraversalOrder.of(context, nullOk: false);
+  FocusTraversalGroup.of(context, nullOk: true);
+  FocusTraversalGroup.of(context, nullOk: false);
+  Focus.of(context, nullOk: true);
+  Focus.of(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.of(context, nullOk: true);
+  Shortcuts.of(context, nullOk: false);
+  Actions.find(context, nullOk: true);
+  Actions.find(context, nullOk: false);
+  Actions.handler(context, nullOk: true);
+  Actions.handler(context, nullOk: false);
+  Actions.invoke(context, nullOk: true);
+  Actions.invoke(context, nullOk: false);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.of(context, nullOk: true);
+  AnimatedList.of(context, nullOk: false);
+  SliverAnimatedList.of(context, nullOk: true);
+  SliverAnimatedList.of(context, nullOk: false);
 }
diff --git a/packages/flutter/test_fixes/widgets.dart.expect b/packages/flutter/test_fixes/widgets.dart.expect
index 92171a8..4f0378c 100644
--- a/packages/flutter/test_fixes/widgets.dart.expect
+++ b/packages/flutter/test_fixes/widgets.dart.expect
@@ -5,6 +5,9 @@
 import 'package:flutter/widgets.dart';
 
 void main() {
+  // Generic reference variables.
+  BuildContext context;
+
   // Changes made in https://github.com/flutter/flutter/pull/44189
   const Element element = Element(myWidget);
   element.dependOnInheritedElement(ancestor);
@@ -48,4 +51,44 @@
   const Stack stack = Stack(clipBehavior: Clip.none);
   const Stack stack = Stack(clipBehavior: Clip.hardEdge);
   final behavior = stack.clipBehavior;
+
+  // Changes made in https://github.com/flutter/flutter/pull/68736
+  MediaQuery.maybeOf(context);
+  MediaQuery.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/70726
+  Navigator.maybeOf(context);
+  Navigator.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68910
+  Router.maybeOf(context);
+  Router.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68911
+  Localizations.maybeLocaleOf(context);
+  Localizations.localeOf(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68917
+  FocusTraversalOrder.maybeOf(context);
+  FocusTraversalOrder.of(context);
+  FocusTraversalGroup.maybeOf(context);
+  FocusTraversalGroup.of(context);
+  Focus.maybeOf(context);
+  Focus.of(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68921
+  Shortcuts.maybeOf(context);
+  Shortcuts.of(context);
+  Actions.maybeFind(context);
+  Actions.find(context);
+  Actions.handler(context);
+  Actions.handler(context);
+  Actions.maybeInvoke(context);
+  Actions.invoke(context);
+
+  // Changes made in https://github.com/flutter/flutter/pull/68925
+  AnimatedList.maybeOf(context);
+  AnimatedList.of(context);
+  SliverAnimatedList.maybeOf(context);
+  SliverAnimatedList.of(context);
 }
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart
index 5d1484c..177ab45 100644
--- a/packages/flutter_tools/lib/src/cache.dart
+++ b/packages/flutter_tools/lib/src/cache.dart
@@ -1789,6 +1789,12 @@
       } finally {
         status.stop();
       }
+      /// Unzipping multiple file into a directory will not remove old files
+      /// from previous versions that are not present in the new bundle.
+      final Directory destination = location.childDirectory(
+        tempFile.fileSystem.path.basenameWithoutExtension(tempFile.path)
+      );
+      ErrorHandlingFileSystem.deleteIfExists(destination, recursive: true);
       _ensureExists(location);
 
       try {
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart
index 3e217ec..4147bd8 100644
--- a/packages/flutter_tools/lib/src/commands/create.dart
+++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -236,6 +236,8 @@
       linux: featureFlags.isLinuxEnabled && platforms.contains('linux'),
       macos: featureFlags.isMacOSEnabled && platforms.contains('macos'),
       windows: featureFlags.isWindowsEnabled && platforms.contains('windows'),
+      // Enable null-safety for sample code, which is - unlike our regular templates - already migrated.
+      dartSdkVersionBounds: sampleCode != null ? '">=2.12.0-0 <3.0.0"' : '">=2.7.0 <3.0.0"'
     );
 
     final String relativeDirPath = globals.fs.path.relative(projectDirPath);
diff --git a/packages/flutter_tools/lib/src/commands/create_base.dart b/packages/flutter_tools/lib/src/commands/create_base.dart
index e8620d7..5b5729f 100644
--- a/packages/flutter_tools/lib/src/commands/create_base.dart
+++ b/packages/flutter_tools/lib/src/commands/create_base.dart
@@ -311,6 +311,7 @@
     String androidLanguage,
     String iosLanguage,
     String flutterRoot,
+    String dartSdkVersionBounds,
     bool withPluginHook = false,
     bool ios = false,
     bool android = false,
@@ -364,6 +365,7 @@
       'macos': macos,
       'windows': windows,
       'year': DateTime.now().year,
+      'dartSdkVersionBounds': dartSdkVersionBounds,
     };
   }
 
diff --git a/packages/flutter_tools/templates/app/pubspec.yaml.tmpl b/packages/flutter_tools/templates/app/pubspec.yaml.tmpl
index 62e7185..2d6d9c4 100644
--- a/packages/flutter_tools/templates/app/pubspec.yaml.tmpl
+++ b/packages/flutter_tools/templates/app/pubspec.yaml.tmpl
@@ -20,7 +20,7 @@
 {{/withPluginHook}}
 
 environment:
-  sdk: ">=2.7.0 <3.0.0"
+  sdk: {{dartSdkVersionBounds}}
 
 dependencies:
   flutter:
diff --git a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl
index 3262c9e..67da1d6 100644
--- a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl
+++ b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl
@@ -18,7 +18,7 @@
 version: 1.0.0+1
 
 environment:
-  sdk: ">=2.1.0 <3.0.0"
+  sdk: {{dartSdkVersionBounds}}
 
 dependencies:
   flutter:
diff --git a/packages/flutter_tools/templates/package/pubspec.yaml.tmpl b/packages/flutter_tools/templates/package/pubspec.yaml.tmpl
index 4943d54..951526c 100644
--- a/packages/flutter_tools/templates/package/pubspec.yaml.tmpl
+++ b/packages/flutter_tools/templates/package/pubspec.yaml.tmpl
@@ -5,7 +5,7 @@
 homepage:
 
 environment:
-  sdk: ">=2.7.0 <3.0.0"
+  sdk: {{dartSdkVersionBounds}}
   flutter: ">=1.17.0"
 
 dependencies:
diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
index c1b28ef..696fc2d 100755
--- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
@@ -1602,6 +1602,20 @@
     HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() {}'),
   });
 
+  testUsingContext('null-safe sample-based project have no analyzer errors', () async {
+    await _createAndAnalyzeProject(
+      projectDir,
+      <String>['--no-pub', '--sample=foo.bar.Baz'],
+      <String>['lib/main.dart'],
+    );
+    expect(
+      projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(),
+      contains('String?'), // uses null-safe syntax
+    );
+  }, overrides: <Type, Generator>{
+    HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() { String? foo; print(foo); }'),
+  });
+
   testUsingContext('can write samples index to disk', () async {
     final String outputFile = globals.fs.path.join(tempDir.path, 'flutter_samples.json');
     final CreateCommand command = CreateCommand();
diff --git a/packages/flutter_tools/test/general.shard/artifact_updater_test.dart b/packages/flutter_tools/test/general.shard/artifact_updater_test.dart
index 46acae6..6ecb664 100644
--- a/packages/flutter_tools/test/general.shard/artifact_updater_test.dart
+++ b/packages/flutter_tools/test/general.shard/artifact_updater_test.dart
@@ -44,6 +44,35 @@
     expect(fileSystem.file('out/test'), exists);
   });
 
+  testWithoutContext('ArtifactUpdater can download a zip archive and delete stale files', () async {
+    final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();
+    final MemoryFileSystem fileSystem = MemoryFileSystem.test();
+    final BufferLogger logger = BufferLogger.test();
+    final ArtifactUpdater artifactUpdater = ArtifactUpdater(
+      fileSystem: fileSystem,
+      logger: logger,
+      operatingSystemUtils: operatingSystemUtils,
+      platform: testPlatform,
+      httpClient: MockHttpClient(),
+      tempStorage: fileSystem.currentDirectory.childDirectory('temp')
+        ..createSync(),
+    );
+    // Unrelated file from another cache.
+    fileSystem.file('out/bar').createSync(recursive: true);
+    // Stale file from current cache.
+    fileSystem.file('out/test/foo.txt').createSync(recursive: true);
+
+    await artifactUpdater.downloadZipArchive(
+      'test message',
+      Uri.parse('http:///test.zip'),
+      fileSystem.currentDirectory.childDirectory('out'),
+    );
+    expect(logger.statusText, contains('test message'));
+    expect(fileSystem.file('out/test'), exists);
+    expect(fileSystem.file('out/bar'), exists);
+    expect(fileSystem.file('out/test/foo.txt'), isNot(exists));
+  });
+
   testWithoutContext('ArtifactUpdater will not validate the md5 hash if the '
     'x-goog-hash header is present but missing an md5 entry', () async {
     final MockOperatingSystemUtils operatingSystemUtils = MockOperatingSystemUtils();