[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();