diff --git a/.ci.yaml b/.ci.yaml index 42797f7697f36..d284a96397779 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -51,7 +51,7 @@ platform_properties: [ {"dependency": "android_sdk", "version": "version:34v3"}, {"dependency": "android_virtual_device", "version": "android_35_google_apis_x64.textpb"}, - {"dependency": "avd_cipd_version", "version": "build_id:8739520057779466577"}, + {"dependency": "avd_cipd_version", "version": "build_id:8735216702926189361"}, {"dependency": "open_jdk", "version": "version:17"} ] os: Ubuntu @@ -68,7 +68,7 @@ platform_properties: [ {"dependency": "android_sdk", "version": "version:34v3"}, {"dependency": "android_virtual_device", "version": "android_34_google_apis_x64.textpb"}, - {"dependency": "avd_cipd_version", "version": "build_id:8739520057779466577"}, + {"dependency": "avd_cipd_version", "version": "build_id:8735216702926189361"}, {"dependency": "open_jdk", "version": "version:17"} ] os: Ubuntu @@ -1005,7 +1005,6 @@ targets: - .ci.yaml - name: Linux build_android_host_app_with_module_aar - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: @@ -1025,7 +1024,6 @@ targets: - .ci.yaml - name: Linux build_android_host_app_with_module_source - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: @@ -1372,7 +1370,6 @@ targets: - name: Linux android_java11_dependency_smoke_tests recipe: devicelab/devicelab_drone - presubmit: false timeout: 60 properties: add_recipes_cq: "true" @@ -1386,10 +1383,12 @@ targets: tags: > ["devicelab", "hostonly", "linux"] test_timeout_secs: "2700" + runIf: + - packages/flutter_tools/templates/** + - .ci.yaml - name: Linux android_java17_dependency_smoke_tests recipe: devicelab/devicelab_drone - presubmit: false timeout: 60 properties: add_recipes_cq: "true" @@ -1403,6 +1402,9 @@ targets: tags: > ["devicelab", "hostonly", "linux"] test_timeout_secs: "2700" + runIf: + - packages/flutter_tools/templates/** + - .ci.yaml - name: Linux tool_tests_commands recipe: flutter/flutter_drone @@ -1460,7 +1462,6 @@ targets: - name: Linux_android_emu_34 flutter_driver_android_test recipe: flutter/flutter_drone - bringup: true # https://github.com/flutter/flutter/issues/156903 timeout: 60 properties: shard: flutter_driver_android @@ -3911,6 +3912,7 @@ targets: - name: Mac framework_tests_impeller recipe: flutter/flutter_drone timeout: 60 + bringup: true # https://github.com/flutter/flutter/issues/157552 properties: cpu: x86 # https://github.com/flutter/flutter/issues/119880 dependencies: >- @@ -4094,7 +4096,6 @@ targets: - .ci.yaml - name: Mac build_android_host_app_with_module_aar - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: @@ -4113,7 +4114,6 @@ targets: - .ci.yaml - name: Mac build_android_host_app_with_module_source - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: @@ -5360,7 +5360,6 @@ targets: - .ci.yaml - name: Windows build_tests_1_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5379,7 +5378,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_2_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5398,7 +5396,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_3_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5417,7 +5414,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_4_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5436,7 +5432,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_5_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5455,7 +5450,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_6_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5474,7 +5468,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_7_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5493,7 +5486,6 @@ targets: ["framework", "hostonly", "shard", "windows"] - name: Windows build_tests_8_8 - bringup: true recipe: flutter/flutter_drone timeout: 60 properties: @@ -5750,7 +5742,6 @@ targets: - .ci.yaml - name: Windows build_android_host_app_with_module_aar - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: @@ -5770,7 +5761,6 @@ targets: - .ci.yaml - name: Windows build_android_host_app_with_module_source - bringup: true recipe: devicelab/devicelab_drone timeout: 60 properties: diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 9d4970f63e076..2165986baf8e9 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -53e187a1d4e4cc2ea6665708b77cb6ec03e3ad7a +88716d804aef8561aaef41e84325a2f9c20c829f diff --git a/bin/internal/flutter_packages.version b/bin/internal/flutter_packages.version index 36c58508a9f83..d8df4620a1512 100644 --- a/bin/internal/flutter_packages.version +++ b/bin/internal/flutter_packages.version @@ -1 +1 @@ -2a1c477a788c1e0cd3e48c96fec97ffcbe5874e0 +a556f0f5282552a9ec892be3ac2b5f657032eec8 diff --git a/bin/internal/fuchsia-linux.version b/bin/internal/fuchsia-linux.version index a6a436061d5b4..0660d113760ef 100644 --- a/bin/internal/fuchsia-linux.version +++ b/bin/internal/fuchsia-linux.version @@ -1 +1 @@ -PWz1ufnYNOGpgw-qgzbmF_8w4vX5MGYflxOPw7YSEmcC +Y4TdnTpLdkI107K42QJpph-xr8_tS-0BVyiEtvu546UC diff --git a/dev/a11y_assessments/pubspec.yaml b/dev/a11y_assessments/pubspec.yaml index 675b4729b89da..6972b5cc90438 100644 --- a/dev/a11y_assessments/pubspec.yaml +++ b/dev/a11y_assessments/pubspec.yaml @@ -23,8 +23,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +39,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml index 544dafd5d16e4..8d6d487894a17 100644 --- a/dev/automated_tests/pubspec.yaml +++ b/dev/automated_tests/pubspec.yaml @@ -34,8 +34,8 @@ dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +76,4 @@ flutter: assets: - icon/test.png -# PUBSPEC CHECKSUM: 8ccc +# PUBSPEC CHECKSUM: 27ce diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml index a84f41810d7e1..5878c9a687d39 100644 --- a/dev/benchmarks/complex_layout/pubspec.yaml +++ b/dev/benchmarks/complex_layout/pubspec.yaml @@ -57,8 +57,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -87,4 +87,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: ada5 +# PUBSPEC CHECKSUM: 54a7 diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml index fdf55d7e05d34..ed3dfb89120e6 100644 --- a/dev/benchmarks/macrobenchmarks/pubspec.yaml +++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml @@ -27,8 +27,8 @@ dependencies: collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -214,4 +214,4 @@ flutter: fonts: - asset: packages/flutter_gallery_assets/fonts/GalleryIcons.ttf -# PUBSPEC CHECKSUM: ada5 +# PUBSPEC CHECKSUM: 54a7 diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml index 427e32a295a47..168df5332219c 100644 --- a/dev/benchmarks/microbenchmarks/pubspec.yaml +++ b/dev/benchmarks/microbenchmarks/pubspec.yaml @@ -37,8 +37,8 @@ dependencies: io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" isolate: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -141,4 +141,4 @@ flutter: - packages/flutter_gallery_assets/people/square/stella.png - packages/flutter_gallery_assets/people/square/trevor.png -# PUBSPEC CHECKSUM: 3ce5 +# PUBSPEC CHECKSUM: 62e7 diff --git a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml index 01940ff595964..bb38ce528491f 100644 --- a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml +++ b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml @@ -36,8 +36,8 @@ dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -78,4 +78,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 3309 +# PUBSPEC CHECKSUM: 590b diff --git a/dev/benchmarks/platform_views_layout/pubspec.yaml b/dev/benchmarks/platform_views_layout/pubspec.yaml index fa25c538df20e..b9634b30c7cc5 100644 --- a/dev/benchmarks/platform_views_layout/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout/pubspec.yaml @@ -60,8 +60,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -90,4 +90,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: 546e +# PUBSPEC CHECKSUM: 7d70 diff --git a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml index aa9ce1538747a..b31304ed5bfe6 100644 --- a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml @@ -55,8 +55,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -85,4 +85,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: ada5 +# PUBSPEC CHECKSUM: 54a7 diff --git a/dev/benchmarks/test_apps/stocks/pubspec.yaml b/dev/benchmarks/test_apps/stocks/pubspec.yaml index 28dd9d7cc8e4d..1731f2c77c0fc 100644 --- a/dev/benchmarks/test_apps/stocks/pubspec.yaml +++ b/dev/benchmarks/test_apps/stocks/pubspec.yaml @@ -48,8 +48,8 @@ dev_dependencies: http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,4 +80,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: da92 +# PUBSPEC CHECKSUM: 8194 diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart index 8bf5836aeb671..49a4e1f0fa149 100644 --- a/dev/bots/analyze.dart +++ b/dev/bots/analyze.dart @@ -2238,6 +2238,7 @@ const Set kExecutableAllowlist = { 'dev/tools/gen_keycodes/bin/gen_keycodes', 'dev/tools/repackage_gradle_wrapper.sh', + 'dev/tools/bin/engine_hash.sh', 'packages/flutter_tools/bin/macos_assemble.sh', 'packages/flutter_tools/bin/tool_backend.sh', diff --git a/dev/bots/check_code_samples.dart b/dev/bots/check_code_samples.dart index 84900f157c357..40dd6c9d43d5c 100644 --- a/dev/bots/check_code_samples.dart +++ b/dev/bots/check_code_samples.dart @@ -309,14 +309,7 @@ class SampleChecker { // TODO(gspencergoog): implement the missing tests. // See https://github.com/flutter/flutter/issues/130459 final Set _knownMissingTests = { - 'examples/api/test/material/bottom_app_bar/bottom_app_bar.2_test.dart', - 'examples/api/test/material/bottom_app_bar/bottom_app_bar.1_test.dart', - 'examples/api/test/material/selectable_region/selectable_region.0_test.dart', 'examples/api/test/material/color_scheme/dynamic_content_color.0_test.dart', - 'examples/api/test/material/platform_menu_bar/platform_menu_bar.0_test.dart', - 'examples/api/test/material/flexible_space_bar/flexible_space_bar.0_test.dart', - 'examples/api/test/material/navigation_rail/navigation_rail.extended_animation.0_test.dart', - 'examples/api/test/painting/star_border/star_border.0_test.dart', 'examples/api/test/widgets/navigator/navigator.restorable_push_and_remove_until.0_test.dart', 'examples/api/test/widgets/navigator/navigator.restorable_push.0_test.dart', 'examples/api/test/widgets/navigator/navigator_state.restorable_push_replacement.0_test.dart', @@ -346,5 +339,4 @@ final Set _knownMissingTests = { 'examples/api/test/widgets/restoration/restoration_mixin.0_test.dart', 'examples/api/test/widgets/actions/focusable_action_detector.0_test.dart', 'examples/api/test/widgets/focus_scope/focus_scope.0_test.dart', - 'examples/api/test/gestures/pointer_signal_resolver/pointer_signal_resolver.0_test.dart', }; diff --git a/dev/bots/test/test_test.dart b/dev/bots/test/test_test.dart index e620de8f3ae2b..2350e9bba48b6 100644 --- a/dev/bots/test/test_test.dart +++ b/dev/bots/test/test_test.dart @@ -4,6 +4,7 @@ import 'dart:io' hide Platform; +import 'package:collection/collection.dart'; import 'package:file/file.dart' as fs; import 'package:file/memory.dart'; import 'package:path/path.dart' as path; @@ -154,4 +155,60 @@ void main() { expect(result.stdout, contains('Invalid subshard name')); }); }); + + test('selectTestsForSubShard distributes tests amongst subshards correctly', () async { + List makeTests(int count) => List.generate(count, (int index) => index); + + void testSubsharding(int testCount, int subshardCount) { + String failureReason(String reason) { + return 'Subsharding test failed for testCount=$testCount, subshardCount=$subshardCount.\n' + '$reason'; + } + + final List tests = makeTests(testCount); + final List> subshards = List>.generate(subshardCount, (int index) { + final int subShardIndex = index + 1; + final (int start, int end) = selectTestsForSubShard( + testCount: tests.length, + subShardIndex: subShardIndex, + subShardCount: subshardCount, + ); + return tests.sublist(start, end); + }); + + final List testedTests = subshards.flattened.toList(); + final Set deduped = Set.from(subshards.flattened); + expect( + testedTests, + hasLength(deduped.length), + reason: failureReason('Subshards may have had duplicate tests.'), + ); + expect( + testedTests, + unorderedEquals(tests), + reason: failureReason('One or more tests were not assigned to a subshard.'), + ); + + final int minimumTestsPerShard = (testCount / subshardCount).floor(); + for (int i = 0; i < subshards.length; i++) { + final int extraTestsInThisShard = subshards[i].length - minimumTestsPerShard; + expect( + extraTestsInThisShard, + isNonNegative, + reason: failureReason( + 'Subsharding uneven. Subshard ${i + 1} had too few tests: ${subshards[i].length}'), + ); + expect( + extraTestsInThisShard, + lessThanOrEqualTo(1), + reason: failureReason( + 'Subsharding uneven. Subshard ${i + 1} had too many tests: ${subshards[i].length}'), + ); + } + } + + testSubsharding(9, 3); + testSubsharding(25, 8); + testSubsharding(30, 15); + }); } diff --git a/dev/bots/utils.dart b/dev/bots/utils.dart index 735b5ef3e7e7c..be8ab9b028dd9 100644 --- a/dev/bots/utils.dart +++ b/dev/bots/utils.dart @@ -11,6 +11,7 @@ import 'dart:math' as math; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/ast/ast.dart'; +import 'package:collection/collection.dart'; import 'package:file/file.dart' as fs; import 'package:file/local.dart'; import 'package:meta/meta.dart'; @@ -176,16 +177,16 @@ Never reportErrorsAndExit(String message) { _hideTimer = null; print('$clock $message$reset'); print(redLine); - print('${red}For your convenience, the error messages reported above are repeated here:$reset'); + print('${red}The error messages reported above are repeated here:$reset'); final bool printSeparators = _errorMessages.any((List messages) => messages.length > 1); if (printSeparators) { - print(' πŸ™™ πŸ™› '); + print(' -- This line intentionally left blank -- '); } for (int index = 0; index < _errorMessages.length * 2 - 1; index += 1) { if (index.isEven) { _errorMessages[index ~/ 2].forEach(print); } else if (printSeparators) { - print(' πŸ™™ πŸ™› '); + print(' -- This line intentionally left blank -- '); } } print(redLine); @@ -534,7 +535,7 @@ Future runShardRunnerIndexOfTotalSubshard(List tests) async { } /// 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 format of SUBSHARD is "{index}_{total number of shards}". /// The scheduler can change the number of total shards without needing an additional /// commit in this repository. /// @@ -569,14 +570,48 @@ List selectIndexOfTotalSubshard(List tests, {String subshardKey = kSubs return []; } - final int testsPerShard = (tests.length / total).ceil(); - final int start = (index - 1) * testsPerShard; - final int end = math.min(index * testsPerShard, tests.length); - + final (int start, int end) = selectTestsForSubShard( + testCount: tests.length, + subShardIndex: index, + subShardCount: total, + ); print('Selecting subshard $index of $total (tests ${start + 1}-$end of ${tests.length})'); return tests.sublist(start, end); } +/// Finds the interval of tests that a subshard is responsible for testing. +@visibleForTesting +(int start, int end) selectTestsForSubShard({ + required int testCount, + required int subShardIndex, + required int subShardCount, +}) { + // While there exists a closed formula figuring out the range of tests the + // subshard is resposible for, modeling this as a simulation of distributing + // items equally into buckets is more intuitive. + // + // A bucket represents how many tests a subshard should be allocated. + final List buckets = List.filled(subShardCount, 0); + // First, allocate an equal number of items to each bucket. + for (int i = 0; i < buckets.length; i++) { + buckets[i] = (testCount / subShardCount).floor(); + } + // For the N leftover items, put one into each of the first N buckets. + final int remainingItems = testCount % buckets.length; + for (int i = 0; i < remainingItems; i++) { + buckets[i] += 1; + } + + // Lastly, compute the indices of the items in buckets[index]. + // We derive this from the toal number items in previous buckets and the number + // of items in this bucket. + final int numberOfItemsInPreviousBuckets = subShardIndex == 0 ? 0 : buckets.sublist(0, subShardIndex - 1).sum; + final int start = numberOfItemsInPreviousBuckets; + final int end = start + buckets[subShardIndex - 1]; + + return (start, end); +} + Future _runFromList(Map items, String key, String name, int positionInTaskName) async { String? item = Platform.environment[key]; if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) { diff --git a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart index fc9c07f32f76a..af6c85e8cf1fc 100644 --- a/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart +++ b/dev/devicelab/bin/tasks/build_ios_framework_module_test.dart @@ -188,15 +188,25 @@ Future _testBuildIosFramework(Directory projectDir, { bool isModule = fals 'vm_snapshot_data', )); + final String dsymPath = path.join( + outputPath, + mode, + 'App.xcframework', + 'ios-arm64', + 'dSYMs' + ); + checkDirectoryExists(dsymPath); + final String appFrameworkDsymPath = path.join( - outputPath, - mode, - 'App.xcframework', - 'ios-arm64', - 'dSYMs', - 'App.framework.dSYM' + dsymPath, + 'App.framework.dSYM' ); checkDirectoryExists(appFrameworkDsymPath); + + if (Directory(dsymPath).listSync().whereType().length != 1) { + throw TaskResult.failure('App.framework/dSYMs should ONLY contain App.xcframework.dSYM'); + } + await _checkDsym(path.join( appFrameworkDsymPath, 'Contents', diff --git a/dev/devicelab/bin/tasks/engine_dependency_proxy_test.dart b/dev/devicelab/bin/tasks/engine_dependency_proxy_test.dart index 9443a29bb257a..c62e393c3cecf 100644 --- a/dev/devicelab/bin/tasks/engine_dependency_proxy_test.dart +++ b/dev/devicelab/bin/tasks/engine_dependency_proxy_test.dart @@ -29,14 +29,17 @@ Future main() async { await runProjectTest((FlutterProject flutterProject) async { await inDirectory(path.join(flutterProject.rootPath, 'android'), () async { section('Insert gradle testing script'); - final File build = File(path.join( - flutterProject.rootPath, 'android', 'app', 'build.gradle', - )); - build.writeAsStringSync( + final File buildFile = getAndroidBuildFile(path.join(flutterProject.rootPath, 'android', 'app')); + buildFile.writeAsStringSync( ''' -task printEngineMavenUrl() { +tasks.register("printEngineMavenUrl") { doLast { - println project.repositories.find { it.name == 'maven' }.url + project.repositories.forEach { repo -> + if (repo.name == "maven") { + repo as MavenArtifactRepository + logger.quiet(repo.url.toString()) + } + } } } ''', diff --git a/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart b/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart index c4887a55018be..4e94b62d03d20 100644 --- a/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart +++ b/dev/devicelab/bin/tasks/flutter_gallery__back_button_memory.dart @@ -32,10 +32,7 @@ class BackButtonMemoryTest extends MemoryTest { // Push back button, wait for it to be seen by the Flutter app. prepareForNextMessage('AppLifecycleState.paused'); await device!.shellExec('input', ['keyevent', 'KEYCODE_BACK']); - - // Note: post UI/platform merge, we consistently miss this message. From - // local logcat it seems to be printed but it does not reach the tool. - await receivedNextMessage?.timeout(const Duration(seconds: 4), onTimeout: () {}); + await receivedNextMessage; // Give Android time to settle (e.g. run GCs) after closing the app. await Future.delayed(const Duration(milliseconds: 100)); diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart index a8f544ec5918f..e9ebfea319c87 100644 --- a/dev/devicelab/lib/framework/apk_utils.dart +++ b/dev/devicelab/lib/framework/apk_utils.dart @@ -256,18 +256,17 @@ class FlutterProject { String get rootPath => path.join(parent.path, name); String get androidPath => path.join(rootPath, 'android'); String get iosPath => path.join(rootPath, 'ios'); + File get appBuildFile => getAndroidBuildFile(path.join(androidPath, 'app')); Future addCustomBuildType(String name, {required String initWith}) async { - final File buildScript = File( - path.join(androidPath, 'app', 'build.gradle'), - ); + final File buildScript = appBuildFile; buildScript.openWrite(mode: FileMode.append).write(''' android { buildTypes { - $name { - initWith $initWith + create("$name") { + initWith(getByName("$initWith")) } } } @@ -288,14 +287,12 @@ android { } Future setMinSdkVersion(int sdkVersion) async { - final File buildScript = File( - path.join(androidPath, 'app', 'build.gradle'), - ); + final File buildScript = appBuildFile; buildScript.openWrite(mode: FileMode.append).write(''' android { defaultConfig { - minSdkVersion $sdkVersion + minSdk = $sdkVersion } } '''); @@ -308,22 +305,20 @@ android { } Future addProductFlavors(Iterable flavors) async { - final File buildScript = File( - path.join(androidPath, 'app', 'build.gradle'), - ); + final File buildScript = appBuildFile; final String flavorConfig = flavors.map((String name) { return ''' -$name { - applicationIdSuffix ".$name" - versionNameSuffix "-$name" +create("$name") { + applicationIdSuffix = ".$name" + versionNameSuffix = "-$name" } '''; }).join('\n'); buildScript.openWrite(mode: FileMode.append).write(''' android { - flavorDimensions "mode" + flavorDimensions.add("mode") productFlavors { $flavorConfig } @@ -332,9 +327,7 @@ android { } Future introduceError() async { - final File buildScript = File( - path.join(androidPath, 'app', 'build.gradle'), - ); + final File buildScript = appBuildFile; await buildScript.writeAsString((await buildScript.readAsString()).replaceAll('buildTypes', 'builTypes')); } @@ -477,3 +470,14 @@ String? validateSnapshotDependency(FlutterProject project, String expectedTarget return contentSnapshot.contains('$expectedTarget ') ? null : 'Dependency file should have $expectedTarget as target. Instead found $contentSnapshot'; } + +File getAndroidBuildFile(String androidAppPath, {bool settings = false}) { + final File groovyFile = File(path.join(androidAppPath, settings ? 'settings.gradle' : 'build.gradle')); + final File kotlinFile = File(path.join(androidAppPath, settings ? 'settings.gradle.kts' : 'build.gradle.kts')); + + if (groovyFile.existsSync()) { + return groovyFile; + } + + return kotlinFile; +} diff --git a/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart b/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart index 2424ac63fcd5d..3c8a32b260c5e 100644 --- a/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart +++ b/dev/devicelab/lib/framework/dependency_smoke_test_task_definition.dart @@ -5,6 +5,8 @@ import 'dart:io'; import 'package:file/local.dart'; + +import 'apk_utils.dart'; import 'task_result.dart'; import 'utils.dart'; @@ -16,13 +18,13 @@ import 'utils.dart'; /// by an easily find/replaceable string. const String gradleSettingsFileContent = r''' pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") @@ -34,12 +36,12 @@ pluginManagement { } plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "AGP_REPLACE_ME" apply false - id "org.jetbrains.kotlin.android" version "KGP_REPLACE_ME" apply false + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "AGP_REPLACE_ME" apply false + id("org.jetbrains.kotlin.android") version "KGP_REPLACE_ME" apply false } -include ":app" +include(":app") '''; @@ -58,7 +60,7 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-GRADLE_REPLACE '''; const String gradleReplacementString = 'GRADLE_REPLACE_ME'; -const String flutterCompileSdkString = 'flutter.compileSdkVersion'; +final RegExp flutterCompileSdkString = RegExp(r'flutter\.compileSdkVersion|flutter\.compileSdk'); /// A simple class containing a Kotlin, Gradle, and AGP version. class VersionTuple { @@ -110,8 +112,7 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ final String appPath = '${innerTempDir.absolute.path}/dependency_checker_app'; if (versions.compileSdkVersion != null) { - final File appGradleBuild = localFileSystem.file(localFileSystem.path.join( - appPath, 'android', 'app', 'build.gradle')); + final File appGradleBuild = getAndroidBuildFile(localFileSystem.path.join(appPath, 'android', 'app')); final String appBuildContent = appGradleBuild.readAsStringSync() .replaceFirst(flutterCompileSdkString, versions.compileSdkVersion!); appGradleBuild.writeAsStringSync(appBuildContent); @@ -126,12 +127,14 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ ); await gradleWrapperProperties.writeAsString(propertyContent, flush: true); - final File gradleSettings = localFileSystem.file(localFileSystem.path.join( - appPath, 'android', 'settings.gradle')); + final File gradleSettingsFile = getAndroidBuildFile( + localFileSystem.path.join(appPath, 'android'), + settings: true, + ); final String settingsContent = gradleSettingsFileContent .replaceFirst(agpReplacementString, versions.agpVersion) .replaceFirst(kgpReplacementString, versions.kotlinVersion); - await gradleSettings.writeAsString(settingsContent, flush: true); + await gradleSettingsFile.writeAsString(settingsContent, flush: true); // Ensure that gradle files exists from templates. diff --git a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml index f4f390d94daa0..02a3dd90f218c 100644 --- a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml +++ b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml @@ -44,8 +44,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -95,4 +95,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 2616 +# PUBSPEC CHECKSUM: 5218 diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml index 9c2f4994e1097..64caafd48c0d9 100644 --- a/dev/integration_tests/android_semantics_testing/pubspec.yaml +++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml @@ -32,8 +32,8 @@ dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -70,4 +70,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 64c3 +# PUBSPEC CHECKSUM: 8ac5 diff --git a/dev/integration_tests/android_verified_input/pubspec.yaml b/dev/integration_tests/android_verified_input/pubspec.yaml index aef39ee6bff03..7b01b80304676 100644 --- a/dev/integration_tests/android_verified_input/pubspec.yaml +++ b/dev/integration_tests/android_verified_input/pubspec.yaml @@ -56,8 +56,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,4 +80,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml index 556f704b782c0..a73d261d3e1d6 100644 --- a/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/android_views/android/app/src/main/AndroidManifest.xml @@ -17,9 +17,6 @@ found in the LICENSE file. --> android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml index 6a4bc7b714cb6..a611635628497 100644 --- a/dev/integration_tests/android_views/pubspec.yaml +++ b/dev/integration_tests/android_views/pubspec.yaml @@ -68,8 +68,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -95,4 +95,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8a38 +# PUBSPEC CHECKSUM: fc3a diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml index f353aea19c659..ef470e4a841ef 100644 --- a/dev/integration_tests/channels/pubspec.yaml +++ b/dev/integration_tests/channels/pubspec.yaml @@ -27,8 +27,8 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -45,4 +45,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 53a3 +# PUBSPEC CHECKSUM: 00a5 diff --git a/dev/integration_tests/deferred_components_test/pubspec.yaml b/dev/integration_tests/deferred_components_test/pubspec.yaml index 1d42c331dc277..249edb3741588 100644 --- a/dev/integration_tests/deferred_components_test/pubspec.yaml +++ b/dev/integration_tests/deferred_components_test/pubspec.yaml @@ -50,8 +50,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -84,4 +84,4 @@ flutter: assets: - customassets/flutter_logo.png -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml index 72eb6dfdbf420..f61c74770e81b 100644 --- a/dev/integration_tests/flavors/pubspec.yaml +++ b/dev/integration_tests/flavors/pubspec.yaml @@ -71,8 +71,8 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: @@ -86,4 +86,4 @@ flutter: flavors: - free -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/dev/integration_tests/flutter_gallery/pubspec.yaml b/dev/integration_tests/flutter_gallery/pubspec.yaml index e1ae544655c4a..5a95ba5690b7c 100644 --- a/dev/integration_tests/flutter_gallery/pubspec.yaml +++ b/dev/integration_tests/flutter_gallery/pubspec.yaml @@ -70,8 +70,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -272,4 +272,4 @@ flutter: - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf -# PUBSPEC CHECKSUM: 6833 +# PUBSPEC CHECKSUM: 1635 diff --git a/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml index 66db29e84aba2..618f2bfc8629b 100644 --- a/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/hybrid_android_views/android/app/src/main/AndroidManifest.xml @@ -17,9 +17,6 @@ found in the LICENSE file. --> android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" android:exported="true"> - diff --git a/dev/integration_tests/hybrid_android_views/pubspec.yaml b/dev/integration_tests/hybrid_android_views/pubspec.yaml index d91f5b862f729..8ee9a063cccd8 100644 --- a/dev/integration_tests/hybrid_android_views/pubspec.yaml +++ b/dev/integration_tests/hybrid_android_views/pubspec.yaml @@ -66,8 +66,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -93,4 +93,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8a38 +# PUBSPEC CHECKSUM: fc3a diff --git a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml index 392fbea6e8b42..2b9eb4c5eeb28 100644 --- a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml +++ b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml @@ -41,8 +41,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -102,4 +102,4 @@ flutter: androidPackage: com.example.iosadd2appflutter iosBundleIdentifier: com.example.iosAdd2appFlutter -# PUBSPEC CHECKSUM: 2616 +# PUBSPEC CHECKSUM: 5218 diff --git a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml index b4ec43f56af37..60bc9c9ffb9a3 100644 --- a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml +++ b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml @@ -43,8 +43,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -94,4 +94,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 5c9c +# PUBSPEC CHECKSUM: 099e diff --git a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml index 3e309bf8a010b..bbe2e042e29ad 100644 --- a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml +++ b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml @@ -50,8 +50,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -81,4 +81,4 @@ flutter: # the material Icons class. uses-material-design: true -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/dev/integration_tests/new_gallery/pubspec.yaml b/dev/integration_tests/new_gallery/pubspec.yaml index 197a35f345fae..480d2a89622c8 100644 --- a/dev/integration_tests/new_gallery/pubspec.yaml +++ b/dev/integration_tests/new_gallery/pubspec.yaml @@ -81,8 +81,8 @@ dev_dependencies: http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -311,4 +311,4 @@ flutter: fonts: - asset: packages/flutter_gallery_assets/fonts/GalleryIcons.ttf -# PUBSPEC CHECKSUM: 9f72 +# PUBSPEC CHECKSUM: ae74 diff --git a/dev/integration_tests/non_nullable/pubspec.yaml b/dev/integration_tests/non_nullable/pubspec.yaml index c944cb8936156..9508faee31122 100644 --- a/dev/integration_tests/non_nullable/pubspec.yaml +++ b/dev/integration_tests/non_nullable/pubspec.yaml @@ -26,8 +26,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -42,4 +42,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 2616 +# PUBSPEC CHECKSUM: 5218 diff --git a/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml index 4729dd2a85c3d..82ac943b3a3ac 100644 --- a/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/release_smoke_test/android/app/src/main/AndroidManifest.xml @@ -21,13 +21,6 @@ found in the LICENSE file. --> android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - - diff --git a/dev/integration_tests/release_smoke_test/pubspec.yaml b/dev/integration_tests/release_smoke_test/pubspec.yaml index 405f0b69ac987..e1f3f19e7637a 100644 --- a/dev/integration_tests/release_smoke_test/pubspec.yaml +++ b/dev/integration_tests/release_smoke_test/pubspec.yaml @@ -23,8 +23,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +36,4 @@ dev_dependencies: test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/dev/integration_tests/spell_check/pubspec.yaml b/dev/integration_tests/spell_check/pubspec.yaml index 9ba12c8f4f83d..903d103dad6ee 100644 --- a/dev/integration_tests/spell_check/pubspec.yaml +++ b/dev/integration_tests/spell_check/pubspec.yaml @@ -57,8 +57,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -108,4 +108,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 2616 +# PUBSPEC CHECKSUM: 5218 diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml index 330ea7a3f97be..04057327ec1c3 100644 --- a/dev/integration_tests/ui/pubspec.yaml +++ b/dev/integration_tests/ui/pubspec.yaml @@ -71,8 +71,8 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: @@ -80,4 +80,4 @@ flutter: assets: - assets/foo.png -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/dev/integration_tests/web_e2e_tests/pubspec.yaml b/dev/integration_tests/web_e2e_tests/pubspec.yaml index 9b9b68d36a6d9..a552a515789c8 100644 --- a/dev/integration_tests/web_e2e_tests/pubspec.yaml +++ b/dev/integration_tests/web_e2e_tests/pubspec.yaml @@ -30,8 +30,8 @@ dependencies: collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -88,4 +88,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 83bc +# PUBSPEC CHECKSUM: 9ebe diff --git a/dev/integration_tests/wide_gamut_test/pubspec.yaml b/dev/integration_tests/wide_gamut_test/pubspec.yaml index 66f5f08990d38..dcb402abdc7a8 100644 --- a/dev/integration_tests/wide_gamut_test/pubspec.yaml +++ b/dev/integration_tests/wide_gamut_test/pubspec.yaml @@ -27,8 +27,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -43,4 +43,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml index 54e7a9281fe11..1303e169ed465 100644 --- a/dev/manual_tests/pubspec.yaml +++ b/dev/manual_tests/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -37,4 +37,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/dev/tools/bin/engine_hash.dart b/dev/tools/bin/engine_hash.dart new file mode 100644 index 0000000000000..b8a520fab6f03 --- /dev/null +++ b/dev/tools/bin/engine_hash.dart @@ -0,0 +1,160 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ---------------------------------- NOTE ---------------------------------- +// +// We must keep the logic in this file consistent with the logic in the +// `engine_hash.sh` script in the same directory to ensure that Flutter +// continues to work across all platforms! +// +// -------------------------------------------------------------------------- + +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:args/args.dart'; +import 'package:crypto/crypto.dart'; + +enum GitRevisionStrategy { + mergeBase, + head, +} + +final RegExp _hashRegex = RegExp(r'^([a-fA-F0-9]+)'); + +final ArgParser parser = ArgParser() + ..addOption( + 'strategy', + abbr: 's', + allowed: ['head', 'mergeBase'], + defaultsTo: 'head', + allowedHelp: { + 'head': 'hash from git HEAD', + 'mergeBase': 'hash from the merge-base of HEAD and upstream/master', + }, + ) + ..addFlag('help', abbr: 'h', negatable: false); + +Never printHelp({String? error}) { + final Stdout out = error != null ? stderr : stdout; + if (error != null) { + out.writeln(error); + out.writeln(); + } + out.writeln(''' +Calculate the hash signature for the Flutter Engine +${parser.usage} +'''); + exit(error != null ? 1 : 0); +} + +Future main(List args) async { + final ArgResults arguments; + try { + arguments = parser.parse(args); + } catch (e) { + printHelp(error: '$e'); + } + + if (arguments.wasParsed('help')) { + printHelp(); + } + + final String result; + try { + result = await engineHash( + (List command) => Process.run( + command.first, + command.sublist(1), + stdoutEncoding: utf8, + ), + revisionStrategy: GitRevisionStrategy.values.byName( + arguments.option('strategy')!, + ), + ); + } catch (e) { + stderr.writeln('Error calculating engine hash: $e'); + return 1; + } + + stdout.writeln(result); + + return 0; +} + +/// Returns the hash signature for the engine source code. +Future engineHash( + Future Function(List command) runProcess, { + GitRevisionStrategy revisionStrategy = GitRevisionStrategy.mergeBase, +}) async { + // First figure out the hash we're working with + final String base; + switch (revisionStrategy) { + case GitRevisionStrategy.head: + base = 'HEAD'; + case GitRevisionStrategy.mergeBase: + final ProcessResult processResult = await runProcess( + [ + 'git', + 'merge-base', + 'upstream/master', + 'HEAD', + ], + ); + + if (processResult.exitCode != 0) { + throw ''' +Unable to find merge-base hash of the repository: +${processResult.stderr}'''; + } + + final Match? baseHash = + _hashRegex.matchAsPrefix(processResult.stdout as String); + if (baseHash?.groupCount != 1) { + throw ''' +Unable to parse merge-base hash of the repository +${processResult.stdout}'''; + } + base = baseHash![1]!; + } + + // List the tree (not the working tree) recursively for the merge-base. + // This is important for future filtering of files, but also do not include + // the developer's changes / in flight PRs. + // The presence `engine` and `DEPS` are signals that you live in a monorepo world. + final ProcessResult processResult = await runProcess( + ['git', 'ls-tree', '-r', base, 'engine', 'DEPS'], + ); + + if (processResult.exitCode != 0) { + throw ''' +Unable to list tree +${processResult.stderr}'''; + } + + // Ensure stable line endings so our hash calculation is stable + final String lsTree = processResult.stdout as String; + if (lsTree.trim().isEmpty) { + throw 'Not in a monorepo'; + } + + final Iterable treeLines = + LineSplitter.split(processResult.stdout as String); + + // We could call `git hash-object --stdin` which would just take the input, calculate the size, + // and then sha1sum it like: `blob $size\0$string'. However, that can have different line endings. + // Instead this is equivalent to: + // git ls-tree -r $(git merge-base upstream/main HEAD) | | sha1sum + final StreamController output = StreamController(); + final ByteConversionSink sink = sha1.startChunkedConversion(output); + for (final String line in treeLines) { + sink.add(utf8.encode(line)); + sink.add([0x0a]); + } + sink.close(); + final Digest digest = await output.stream.first; + + return '$digest'; +} diff --git a/dev/tools/bin/engine_hash.sh b/dev/tools/bin/engine_hash.sh new file mode 100755 index 0000000000000..c3e71bee6aedb --- /dev/null +++ b/dev/tools/bin/engine_hash.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Copyright 2014 The Flutter Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# ---------------------------------- NOTE ---------------------------------- # +# +# We must keep the logic in this file consistent with the logic in the +# `engine_hash.dart` script in the same directory to ensure that Flutter +# continues to work across all platforms! +# +# -------------------------------------------------------------------------- # + +# TODO(codefu): Add a test that this always outputs the same hash as +# `engine_hash.dart` when the repositories are merged + +STRATEGY=head + +HELP=$( + cat <\n +\t\tthead: hash from git HEAD\n +\t\tmergeBase: hash from the merge-base of HEAD and upstream/master\n +EOF +) + +function print_help() { + if [ "${1:-0}" -eq 0 ]; then + echo -e $HELP + exit 0 + else + echo >&2 -e $HELP + exit $1 + fi +} + +while [[ "$#" -gt 0 ]]; do + case $1 in + -s | --strategy) + STRATEGY="$2" + shift # past argument + shift # past value + ;; + -h | --help) + print_help + ;; + -* | --*) + echo >&2 -e "Unknown option $1\n" + print_help 1 + ;; + esac +done + +BASE=HEAD +case $STRATEGY in +head) ;; +mergeBase) + BASE=$(git merge-base upstream/master HEAD) + ;; +*) + echo >&2 -e "Unknown strategy $1\n" + print_help 1 + ;; +esac + +LSTREE=$(git ls-tree -r $BASE engine DEPS) +if [ ${#LSTREE} -eq 0 ]; then + echo >&2 Error calculating engine hash: Not in a monorepo + exit 1 +else + HASH=$(echo "$LSTREE" | sha1sum | head -c 40) + echo $HASH +fi diff --git a/dev/tools/native_driver/pubspec.yaml b/dev/tools/native_driver/pubspec.yaml index bedfb9bc268d2..cb7007c15a6f7 100644 --- a/dev/tools/native_driver/pubspec.yaml +++ b/dev/tools/native_driver/pubspec.yaml @@ -27,8 +27,8 @@ dependencies: crypto: 3.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +76,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: d614 +# PUBSPEC CHECKSUM: f116 diff --git a/dev/tools/test/engine_hash_test.dart b/dev/tools/test/engine_hash_test.dart new file mode 100644 index 0000000000000..fac29b91a2bbd --- /dev/null +++ b/dev/tools/test/engine_hash_test.dart @@ -0,0 +1,102 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io' as io; + +import 'package:collection/collection.dart'; +import 'package:test/test.dart'; + +import '../bin/engine_hash.dart' show GitRevisionStrategy, engineHash; + +void main() { + test('Produces an engine hash for merge-base', () async { + final Future Function(List) runProcess = + _fakeProcesses(processes: [ + ( + exe: 'git', + command: 'merge-base', + rest: ['upstream/master', 'HEAD'], + exitCode: 0, + stdout: 'abcdef1234', + stderr: null + ), + ( + exe: 'git', + command: 'ls-tree', + rest: ['-r', 'abcdef1234', 'engine', 'DEPS'], + exitCode: 0, + stdout: 'one\r\ntwo\r\n', + stderr: null + ), + ]); + + final Future result = engineHash(runProcess); + + expect(result, completion('c708d7ef841f7e1748436b8ef5670d0b2de1a227')); + }); + + test('Produces an engine hash for HEAD', () async { + final Future Function(List) runProcess = + _fakeProcesses( + processes: [ + ( + exe: 'git', + command: 'ls-tree', + rest: ['-r', 'HEAD', 'engine', 'DEPS'], + exitCode: 0, + stdout: 'one\ntwo\n', + stderr: null + ), + ], + ); + + final Future result = + engineHash(runProcess, revisionStrategy: GitRevisionStrategy.head); + + expect(result, completion('c708d7ef841f7e1748436b8ef5670d0b2de1a227')); + }); + + test('Returns error in non-monorepo', () async { + final Future Function(List) runProcess = + _fakeProcesses(processes: [ + ( + exe: 'git', + command: 'ls-tree', + rest: ['-r', 'HEAD', 'engine', 'DEPS'], + exitCode: 0, + stdout: '', + stderr: null + ), + ]); + + final Future result = + engineHash(runProcess, revisionStrategy: GitRevisionStrategy.head); + + expect(result, throwsA('Not in a monorepo')); + }); +} + +typedef FakeProcess = ({ + String exe, + String command, + List rest, + dynamic stdout, + dynamic stderr, + int exitCode +}); + +Future Function(List) _fakeProcesses({ + required List processes, +}) => + (List cmd) async { + for (final FakeProcess process in processes) { + if (process.exe.endsWith(cmd[0]) && + process.command.endsWith(cmd[1]) && + process.rest.equals(cmd.sublist(2))) { + return io.ProcessResult( + 1, process.exitCode, process.stdout, process.stderr); + } + } + return io.ProcessResult(1, -42, '', '404 command not found: $cmd'); + }; diff --git a/dev/tools/vitool/pubspec.yaml b/dev/tools/vitool/pubspec.yaml index 0c9afdd54746d..e08b279cf3e90 100644 --- a/dev/tools/vitool/pubspec.yaml +++ b/dev/tools/vitool/pubspec.yaml @@ -27,8 +27,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +40,4 @@ dev_dependencies: test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 1315 +# PUBSPEC CHECKSUM: af17 diff --git a/dev/tracing_tests/pubspec.yaml b/dev/tracing_tests/pubspec.yaml index a8a13d7acc8d7..08124db956b1a 100644 --- a/dev/tracing_tests/pubspec.yaml +++ b/dev/tracing_tests/pubspec.yaml @@ -24,8 +24,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +36,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/docs/ecosystem/contributing/README.md b/docs/ecosystem/contributing/README.md index 14cdf21cf84b2..659aeefad1730 100644 --- a/docs/ecosystem/contributing/README.md +++ b/docs/ecosystem/contributing/README.md @@ -114,7 +114,7 @@ Because we prefer to minimize breaking changes to packages after 1.0, breaking c ## Dependencies -We try to minimize external package dependencies as much as possible, where "external" means packages that are not generally within the control of the Flutter or Dart teams (Examples non-external packages include `sdk:` dependencies, packages in flutter/packages, and packages published by dart.dev), or by an organization with a track record of strong support for both engineering best practices and Flutter. This is for several reasons: +We try to minimize external package dependencies as much as possible, where "external" means packages that are not generally within the control of the Flutter or Dart teams (Examples of non-external packages include `sdk:` dependencies, packages in flutter/packages, and packages published by dart.dev), or by an organization with a track record of strong support for both engineering best practices and Flutter. This is for several reasons: - Maintainability: - We have a policy of always supporting Flutter `master`; if we can't guarantee that fixes for breaking changes can be landed immediately in dependencies, it can block our roller. - If a package is abandoned by its authors, we will have to fork or migrate in order to unblock the entire repository. @@ -125,12 +125,21 @@ If you are considering adding an external dependency: - Consider other options, and discuss with #hackers-ecosystem in Discord. - If you add a `dev_dependency` on an external package, pin it to a specific version if at all possible. - If you add a `dependency` on an external package in an `example/`, pin it to a specific version if at all possible. -- Some dependencies should only be linked as dev_dependencies like integration_test +- If you add a non-dev, non-example dependency on an external package, a formal approval is required, and all of the following must be true: + - The dependency is necessary for a high-priority product/feature. + - The code has an OSS license, and the original source repository is available. + - The dependency has an exact upper bound set to a version that has been reviewed by the Flutter team + - The ecosystem TL and PM are confident that the code could be forked and maintained by the Flutter team if it were to become necessary (e.g., if the package were later abandoned). ### Native dependencies The same general principles apply to native dependencies for plugins (e.g., dependencies specified in an Android Gradle file or iOS podspec file): minimize dependenciesβ€”and especially non-test dependenciesβ€”on libraries that are not created by either the platform vendor or another organization with a track record of strong support for engineering best practices in whom we can have a very high degree of confidence in ongoing support and prompt updates. +### Dev dependencies + +Some dependencies should only be linked as dev_dependencies, such as `integration_test`. Known dev-only dependencies are checked by CI, and if you are adding a new dev-only dependency, consider adding it to +[the repository tooling checks](https://github.com/flutter/packages/blob/main/script/tool/lib/src/pubspec_check_command.dart). + ## Platform Support The goal is to have any plugin feature work on every platform on which that feature makes sense; having a lot of features that are only partially implemented across platforms is often confusing and frustrating for developers trying to use those plugins. However, a single developer will not always have the expertise to implement a feature across all supported platforms. diff --git a/docs/tool/README.md b/docs/tool/README.md index ed4a23fbd9808..5f530adce9560 100644 --- a/docs/tool/README.md +++ b/docs/tool/README.md @@ -53,10 +53,12 @@ dart --enable-asserts dev/bots/analyze.dart ## Making changes to the `flutter` tool -If you want to alter and re-test the tool's behavior itself, locally commit your tool change -in git and the tool will be rebuilt from Dart sources in `packages/flutter_tools` the next -time you run `flutter`. Alternatively, delete the `bin/cache/flutter_tools.snapshot` file. -Doing so will force a rebuild of the tool from your local sources the next time you run `flutter`. +You can run the tool from source by running `bin/flutter-dev`. + +Alternatively, delete the `bin/cache/flutter_tools.snapshot` file or locally commit +your change in git and then run `flutter` again. This will rebuild the tool +from local source. + This step is not required if you are launching `flutter_tools.dart` (either by running or testing) from an IDE. The `flutter_tools` tests run inside the Dart command line VM rather than in the @@ -78,7 +80,7 @@ under "Environment Variables" section, enter `FLUTTER_ROOT=directory_to_your_flu The pre-built flutter tool runs in release mode with the observatory off by default. To enable debugging mode and the observatory on the `flutter` tool, uncomment the -`FLUTTER_TOOL_ARGS` line in the `bin/flutter` shell script. +`FLUTTER_TOOL_ARGS` line in the `bin/flutter` (or `bin/flutter-dev`) shell script. ## Debugging the `flutter` command-line tool in VS Code diff --git a/examples/api/lib/cupertino/segmented_control/cupertino_segmented_control.0.dart b/examples/api/lib/cupertino/segmented_control/cupertino_segmented_control.0.dart index caad200d02637..776fe172f1636 100644 --- a/examples/api/lib/cupertino/segmented_control/cupertino_segmented_control.0.dart +++ b/examples/api/lib/cupertino/segmented_control/cupertino_segmented_control.0.dart @@ -37,6 +37,9 @@ class SegmentedControlExample extends StatefulWidget { class _SegmentedControlExampleState extends State { Sky _selectedSegment = Sky.midnight; + bool _toggleOne = false; + bool _toggleAll = true; + Set _disabledChildren = {}; @override Widget build(BuildContext context) { @@ -45,6 +48,7 @@ class _SegmentedControlExampleState extends State { navigationBar: CupertinoNavigationBar( // This Cupertino segmented control has the enum "Sky" as the type. middle: CupertinoSegmentedControl( + disabledChildren: _disabledChildren, selectedColor: skyColors[_selectedSegment], // Provide horizontal padding around the children. padding: const EdgeInsets.symmetric(horizontal: 12), @@ -73,9 +77,60 @@ class _SegmentedControlExampleState extends State { ), ), child: Center( - child: Text( - 'Selected Segment: ${_selectedSegment.name}', - style: const TextStyle(color: CupertinoColors.white), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Selected Segment: ${_selectedSegment.name}', + style: const TextStyle(color: CupertinoColors.white), + ), + const SizedBox(height: 20), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Disable one segment', style: TextStyle(color: CupertinoColors.white)), + CupertinoSwitch( + value: _toggleOne, + onChanged: (bool value) { + setState(() { + _toggleOne = value; + if (value) { + _toggleAll = false; + _disabledChildren = {Sky.midnight}; + } else { + _toggleAll = true; + _disabledChildren = {}; + } + }); + }, + ), + ], + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Toggle all segments', style: TextStyle(color: CupertinoColors.white)), + CupertinoSwitch( + value: _toggleAll, + onChanged: (bool value) { + setState(() { + _toggleAll = value; + if (value) { + _toggleOne = false; + _disabledChildren = {}; + } else { + _disabledChildren = { + Sky.midnight, + Sky.viridian, + Sky.cerulean, + }; + } + }); + }, + ), + ], + ), + ], ), ), ); diff --git a/examples/api/lib/material/date_picker/show_date_picker.1.dart b/examples/api/lib/material/date_picker/show_date_picker.1.dart new file mode 100644 index 0000000000000..936ff4866e915 --- /dev/null +++ b/examples/api/lib/material/date_picker/show_date_picker.1.dart @@ -0,0 +1,66 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Flutter code sample for basic [showDatePicker]. + +void main() => runApp(const DatePickerApp()); + +class DatePickerApp extends StatelessWidget { + const DatePickerApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('showDatePicker Example')), + body: const Center(child: DatePickerExample()), + ), + ); + } +} + +class DatePickerExample extends StatefulWidget { + const DatePickerExample({super.key}); + + @override + State createState() => _DatePickerExampleState(); +} + +class _DatePickerExampleState extends State { + DateTime? selectedDate; + + Future _selectDate() async { + final DateTime? pickedDate = await showDatePicker( + context: context, + initialDate: DateTime(2021, 7, 25), + firstDate: DateTime(2021), + lastDate: DateTime(2022), + ); + + setState(() { + selectedDate = pickedDate; + }); + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + spacing: 20, + children: [ + Text( + selectedDate != null + ? '${selectedDate!.day}/${selectedDate!.month}/${selectedDate!.year}' + : 'No date selected', + ), + OutlinedButton( + onPressed: _selectDate, + child: const Text('Select Date'), + ), + ], + ); + } +} diff --git a/examples/api/lib/material/flexible_space_bar/flexible_space_bar.0.dart b/examples/api/lib/material/flexible_space_bar/flexible_space_bar.0.dart index b1649afa0b93b..35d6171014122 100644 --- a/examples/api/lib/material/flexible_space_bar/flexible_space_bar.0.dart +++ b/examples/api/lib/material/flexible_space_bar/flexible_space_bar.0.dart @@ -2,77 +2,83 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; /// Flutter code sample for [FlexibleSpaceBar]. -void main() => runApp(const MaterialApp(home: FlexibleSpaceBarExampleApp())); +void main() => runApp(const FlexibleSpaceBarExampleApp()); class FlexibleSpaceBarExampleApp extends StatelessWidget { const FlexibleSpaceBarExampleApp({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - body: CustomScrollView( - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - SliverAppBar( - stretch: true, - onStretchTrigger: () { - // Function callback for stretch - return Future.value(); - }, - expandedHeight: 300.0, - flexibleSpace: FlexibleSpaceBar( - stretchModes: const [ - StretchMode.zoomBackground, - StretchMode.blurBackground, - StretchMode.fadeTitle, - ], - centerTitle: true, - title: const Text('Flight Report'), - background: Stack( - fit: StackFit.expand, - children: [ - Image.network( - 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg', - fit: BoxFit.cover, - ), - const DecoratedBox( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment(0.0, 0.5), - end: Alignment.center, - colors: [ - Color(0x60000000), - Color(0x00000000), - ], + return MaterialApp( + scrollBehavior: const MaterialScrollBehavior().copyWith( + dragDevices: PointerDeviceKind.values.toSet(), + ), + home: Scaffold( + body: CustomScrollView( + physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + slivers: [ + SliverAppBar( + stretch: true, + onStretchTrigger: () { + // Function callback for stretch + return Future.value(); + }, + expandedHeight: 300.0, + flexibleSpace: FlexibleSpaceBar( + stretchModes: const [ + StretchMode.zoomBackground, + StretchMode.blurBackground, + StretchMode.fadeTitle, + ], + centerTitle: true, + title: const Text('Flight Report'), + background: Stack( + fit: StackFit.expand, + children: [ + Image.network( + 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg', + fit: BoxFit.cover, + ), + const DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment(0.0, 0.5), + end: Alignment.center, + colors: [ + Color(0x60000000), + Color(0x00000000), + ], + ), ), ), + ], + ), + ), + ), + SliverList( + delegate: SliverChildListDelegate( + const [ + ListTile( + leading: Icon(Icons.wb_sunny), + title: Text('Sunday'), + subtitle: Text('sunny, h: 80, l: 65'), + ), + ListTile( + leading: Icon(Icons.wb_sunny), + title: Text('Monday'), + subtitle: Text('sunny, h: 80, l: 65'), ), + // ListTiles++ ], ), ), - ), - SliverList( - delegate: SliverChildListDelegate( - const [ - ListTile( - leading: Icon(Icons.wb_sunny), - title: Text('Sunday'), - subtitle: Text('sunny, h: 80, l: 65'), - ), - ListTile( - leading: Icon(Icons.wb_sunny), - title: Text('Monday'), - subtitle: Text('sunny, h: 80, l: 65'), - ), - // ListTiles++ - ], - ), - ), - ], + ], + ), ), ); } diff --git a/examples/api/lib/material/selectable_region/selectable_region.0.dart b/examples/api/lib/material/selectable_region/selectable_region.0.dart index 3442f72c8fd3d..72925863a1320 100644 --- a/examples/api/lib/material/selectable_region/selectable_region.0.dart +++ b/examples/api/lib/material/selectable_region/selectable_region.0.dart @@ -322,6 +322,8 @@ class _RenderSelectableAdapter extends RenderProxyBox with Selectable, Selection @override void dispose() { _geometry.dispose(); + _startHandle = null; + _endHandle = null; super.dispose(); } } diff --git a/examples/api/lib/material/widget_state_input_border/widget_state_input_border.0.dart b/examples/api/lib/material/widget_state_input_border/widget_state_input_border.0.dart new file mode 100644 index 0000000000000..3c9849ca39198 --- /dev/null +++ b/examples/api/lib/material/widget_state_input_border/widget_state_input_border.0.dart @@ -0,0 +1,114 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +/// Flutter code sample for [WidgetStateInputBorder]. + +void main() => runApp(const WidgetStateInputBorderExampleApp()); + +/// This extension isn't necessary when WidgetState properties are +/// configured using [WidgetStateMapper] objects. +/// +/// But sometimes it makes sense to use a resolveWith() callback, +/// and these getters make those callbacks a bit more readable! +extension WidgetStateHelpers on Set { + bool get focused => contains(WidgetState.focused); + bool get hovered => contains(WidgetState.hovered); + bool get disabled => contains(WidgetState.disabled); +} + +class WidgetStateInputBorderExampleApp extends StatelessWidget { + const WidgetStateInputBorderExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('WidgetStateInputBorder Example'), + ), + body: const Center(child: PageContent()), + ), + ); + } +} + +class PageContent extends StatefulWidget { + const PageContent({super.key}); + + @override + State createState() => _PageContentState(); +} + +class _PageContentState extends State { + bool enabled = false; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Spacer(flex: 8), + Focus( + child: WidgetStateInputBorderExample(enabled: enabled), + ), + const Spacer(), + FilterChip( + label: const Text('enable text field'), + selected: enabled, + onSelected: (bool selected) { + setState(() { + enabled = selected; + }); + }, + ), + const Spacer(flex: 8), + ], + ); + } +} + +class WidgetStateInputBorderExample extends StatelessWidget { + const WidgetStateInputBorderExample({super.key, required this.enabled}); + + final bool enabled; + + /// A global or static function can be referenced in a `const` constructor, + /// such as [WidgetStateInputBorder.resolveWith]. + /// + /// Constant values can be useful for promoting accurate equality checks, + /// such as when rebuilding a [Theme] widget. + static UnderlineInputBorder veryCoolBorder(Set states) { + if (states.disabled) { + return const UnderlineInputBorder( + borderSide: BorderSide(color: Colors.grey), + ); + } + + const Color dullViolet = Color(0xFF502080); + + return UnderlineInputBorder( + borderSide: BorderSide( + width: states.hovered ? 6 : (states.focused ? 3 : 1.5), + color: states.focused ? Colors.deepPurpleAccent : dullViolet, + ), + ); + } + + @override + Widget build(BuildContext context) { + final InputDecoration decoration = InputDecoration( + border: const WidgetStateInputBorder.resolveWith(veryCoolBorder), + labelText: enabled ? 'Type something awesome…' : '(click below to enable)', + ); + + return AnimatedFractionallySizedBox( + duration: Durations.medium1, + curve: Curves.ease, + widthFactor: Focus.of(context).hasFocus ? 0.9 : 0.6, + child: TextField(decoration: decoration, enabled: enabled), + ); + } +} diff --git a/examples/api/pubspec.yaml b/examples/api/pubspec.yaml index dd8ccdd28b53d..5c5c37a861e1a 100644 --- a/examples/api/pubspec.yaml +++ b/examples/api/pubspec.yaml @@ -54,8 +54,8 @@ dev_dependencies: intl: 0.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -94,4 +94,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: cef8 +# PUBSPEC CHECKSUM: e9fa diff --git a/examples/api/test/cupertino/segmented_control/cupertino_segmented_control.0_test.dart b/examples/api/test/cupertino/segmented_control/cupertino_segmented_control.0_test.dart index 3eb15abb6fa6a..4b30f7e02fd58 100644 --- a/examples/api/test/cupertino/segmented_control/cupertino_segmented_control.0_test.dart +++ b/examples/api/test/cupertino/segmented_control/cupertino_segmented_control.0_test.dart @@ -2,10 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/cupertino.dart'; import 'package:flutter_api_samples/cupertino/segmented_control/cupertino_segmented_control.0.dart' as example; import 'package:flutter_test/flutter_test.dart'; void main() { + testWidgets('Verify initial state', (WidgetTester tester) async { + await tester.pumpWidget( + const example.SegmentedControlApp(), + ); + + // Midnight is the default selected segment. + expect(find.text('Selected Segment: midnight'), findsOneWidget); + + // All segments are enabled and can be selected. + await tester.tap(find.text('Viridian')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: viridian'), findsOneWidget); + + await tester.tap(find.text('Cerulean')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: cerulean'), findsOneWidget); + + await tester.tap(find.text('Midnight')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: midnight'), findsOneWidget); + + // Verify that the first CupertinoSwitch is off. + final Finder firstSwitchFinder = find.byType(CupertinoSwitch).first; + final CupertinoSwitch firstSwitch = tester.widget(firstSwitchFinder); + expect(firstSwitch.value, false); + + // Verify that the second CupertinoSwitch is on. + final Finder secondSwitchFinder = find.byType(CupertinoSwitch).last; + final CupertinoSwitch secondSwitch = tester.widget(secondSwitchFinder); + expect(secondSwitch.value, true); + }); + testWidgets('Can change a selected segmented control', (WidgetTester tester) async { await tester.pumpWidget( const example.SegmentedControlApp(), @@ -18,4 +51,50 @@ void main() { expect(find.text('Selected Segment: cerulean'), findsOneWidget); }); + + testWidgets('Can not select on a disabled segment', (WidgetTester tester) async { + await tester.pumpWidget( + const example.SegmentedControlApp(), + ); + + // Toggle on the first CupertinoSwitch to disable the first segment. + final Finder firstSwitchFinder = find.byType(CupertinoSwitch).first; + await tester.tap(firstSwitchFinder); + await tester.pumpAndSettle(); + final CupertinoSwitch firstSwitch = tester.widget(firstSwitchFinder); + expect(firstSwitch.value, true); + + // Tap on the second segment then tap back on the first segment. + // Verify that the selected segment is still the second segment. + await tester.tap(find.text('Viridian')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: viridian'), findsOneWidget); + + await tester.tap(find.text('Midnight')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: viridian'), findsOneWidget); + }); + + testWidgets('Can not select on all disabled segments', (WidgetTester tester) async { + await tester.pumpWidget( + const example.SegmentedControlApp(), + ); + + // Toggle off the second CupertinoSwitch to disable all segments. + final Finder secondSwitchFinder = find.byType(CupertinoSwitch).last; + await tester.tap(secondSwitchFinder); + await tester.pumpAndSettle(); + final CupertinoSwitch secondSwitch = tester.widget(secondSwitchFinder); + expect(secondSwitch.value, false); + + // Tap on the second segment and verify that the selected segment is still the first segment. + await tester.tap(find.text('Viridian')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: midnight'), findsOneWidget); + + // Tap on the third segment and verify that the selected segment is still the first segment. + await tester.tap(find.text('Cerulean')); + await tester.pumpAndSettle(); + expect(find.text('Selected Segment: midnight'), findsOneWidget); + }); } diff --git a/examples/api/test/gestures/pointer_signal_resolver/pointer_signal_resolver.0_test.dart b/examples/api/test/gestures/pointer_signal_resolver/pointer_signal_resolver.0_test.dart new file mode 100644 index 0000000000000..ad9105de4200b --- /dev/null +++ b/examples/api/test/gestures/pointer_signal_resolver/pointer_signal_resolver.0_test.dart @@ -0,0 +1,78 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/gestures/pointer_signal_resolver/pointer_signal_resolver.0.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + ({Color outer, Color inner}) getColors(WidgetTester tester) { + final DecoratedBox outerBox = tester.widget(find.byType(DecoratedBox).first); + final DecoratedBox innerBox = tester.widget(find.byType(DecoratedBox).last); + return ( + outer: (outerBox.decoration as BoxDecoration).color!, + inner: (innerBox.decoration as BoxDecoration).color!, + ); + } + + testWidgets('Scrolling on the boxes changes their color', (WidgetTester tester) async { + await tester.pumpWidget( + const example.PointerSignalResolverExampleApp(), + ); + + expect( + getColors(tester), + (outer: const Color(0x3300ff00), inner: const Color(0xffffff00)), + ); + + // Scroll on the outer box. + final TestPointer pointer = TestPointer(1, PointerDeviceKind.mouse); + pointer.hover(const Offset(100, 300)); + await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 100.0))); + await tester.pump(); + + // The outer box changes color. + ({Color inner, Color outer}) colors = getColors(tester); + expect(colors.outer, const Color(0x3300ff0d)); + expect(colors.inner, const Color(0xffffff00)); + + // Scroll on the inner box. + pointer.hover(const Offset(400, 300)); + await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 100.0))); + await tester.pump(); + + // Both boxes change color. + colors = getColors(tester); + expect(colors.outer, const Color(0x3300ff1a)); + expect(colors.inner, const Color(0xfff2ff00)); + + // Use PointerSignalResolver. + await tester.tap(find.byType(Switch)); + await tester.pump(); + + pointer.hover(const Offset(100, 300)); + await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 100.0))); + await tester.pump(); + + // The outer box changes color. + colors = getColors(tester); + expect(colors.outer, const Color(0x3300ff26)); + expect(colors.inner, const Color(0xfff2ff00)); + + // Scroll on the inner box. + pointer.hover(const Offset(400, 300)); + await tester.sendEventToBinding(pointer.scroll(const Offset(0.0, 100.0))); + await tester.pump(); + + // The inner box changes color. + colors = getColors(tester); + expect( + colors.outer, + const Color(0x3300ff26), + ); + expect(colors.inner, const Color(0xffe5ff00)); + }); +} diff --git a/examples/api/test/material/bottom_app_bar/bottom_app_bar.1_test.dart b/examples/api/test/material/bottom_app_bar/bottom_app_bar.1_test.dart new file mode 100644 index 0000000000000..c4b1fe606f46e --- /dev/null +++ b/examples/api/test/material/bottom_app_bar/bottom_app_bar.1_test.dart @@ -0,0 +1,70 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/bottom_app_bar/bottom_app_bar.1.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets( + 'BottomAppBarDemo shows FloatingActionButton and responds to toggle', + (WidgetTester tester) async { + await tester.pumpWidget(const example.BottomAppBarDemo()); + + expect(find.byType(FloatingActionButton), findsOneWidget); + + // Tap the 'Floating Action Button' switch to hide the FAB. + await tester.tap(find.byType(SwitchListTile).first); + await tester.pumpAndSettle(); + + expect(find.byType(FloatingActionButton), findsNothing); + }, + ); + + testWidgets('Notch can be toggled on and off', (WidgetTester tester) async { + await tester.pumpWidget(const example.BottomAppBarDemo()); + + // Check the BottomAppBar has a notch initially. + BottomAppBar bottomAppBar = tester.widget(find.byType(BottomAppBar)); + expect(bottomAppBar.shape, isNotNull); + + // Toggle the 'Notch' switch to remove the notch. + await tester.tap(find.byType(SwitchListTile).last); + await tester.pump(); + + bottomAppBar = tester.widget(find.byType(BottomAppBar)); + expect(bottomAppBar.shape, isNull); + }); + + testWidgets('FAB location can be changed', (WidgetTester tester) async { + await tester.pumpWidget(const example.BottomAppBarDemo()); + + final Offset initialPosition = + tester.getCenter(find.byType(FloatingActionButton)); + + // Verify the initial position is near the right side (docked to the end). + final Size screenSize = tester.getSize( + find.byType(Scaffold), + ); + expect( + initialPosition.dx, + greaterThan(screenSize.width * 0.5), + ); + + // Tap the radio button to move the FAB to centerDocked. + await tester.tap(find.text('Docked - Center')); + await tester.pumpAndSettle(); + + // Get the new FAB position (centerDocked). + final Offset newPosition = tester.getCenter( + find.byType(FloatingActionButton), + ); + + expect( + newPosition.dx, + closeTo(screenSize.width * 0.5, 10), // Center of the screen. + ); + }); +} diff --git a/examples/api/test/material/bottom_app_bar/bottom_app_bar.2_test.dart b/examples/api/test/material/bottom_app_bar/bottom_app_bar.2_test.dart new file mode 100644 index 0000000000000..fd299f6c3bb2e --- /dev/null +++ b/examples/api/test/material/bottom_app_bar/bottom_app_bar.2_test.dart @@ -0,0 +1,95 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/bottom_app_bar/bottom_app_bar.2.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets( + 'Floating Action Button visibility can be toggled', + (WidgetTester tester) async { + await tester.pumpWidget( + const example.BottomAppBarDemo(), + ); + + expect(find.byType(FloatingActionButton), findsOneWidget); + + // Tap the switch to hide the FAB. + await tester.tap(find.byType(SwitchListTile).first); + await tester.pumpAndSettle(); + + expect(find.byType(FloatingActionButton), findsNothing); + }, + ); + + testWidgets('BottomAppBar elevation can be toggled', + (WidgetTester tester) async { + // Build the app. + await tester.pumpWidget(const example.BottomAppBarDemo()); + + // Verify the BottomAppBar has elevation initially. + BottomAppBar bottomAppBar = tester.widget( + find.byType(BottomAppBar), + ); + expect(bottomAppBar.elevation, isNot(0.0)); + + await tester.tap(find.text('Bottom App Bar Elevation')); + await tester.pumpAndSettle(); + + bottomAppBar = tester.widget( + find.byType(BottomAppBar), + ); + expect(bottomAppBar.elevation, equals(0.0)); + }); + + testWidgets( + 'BottomAppBar hides on scroll down and shows on scroll up', + (WidgetTester tester) async { + await tester.pumpWidget(const example.BottomAppBarDemo()); + + // Ensure the BottomAppBar is visible initially. + expect(find.byType(BottomAppBar), findsOneWidget); + + // Scroll down to hide the BottomAppBar. + await tester.drag( + find.byType(ListView), + const Offset(0, -300), + ); + await tester.pumpAndSettle(); + + // Verify the BottomAppBar is hidden. + final Size hiddenSize = tester.getSize( + find.byType(AnimatedContainer), + ); + expect(hiddenSize.height, equals(0.0)); // AnimatedContainer's height + + // Scroll up to show the BottomAppBar again. + await tester.drag(find.byType(ListView), const Offset(0, 300)); + await tester.pumpAndSettle(); + + // Verify the BottomAppBar is visible again. + final Size visibleSize = tester.getSize( + find.byType(AnimatedContainer), + ); + expect(visibleSize.height, equals(80.0)); + }, + ); + + testWidgets( + 'SnackBar is shown when Open popup menu is pressed', + (WidgetTester tester) async { + await tester.pumpWidget(const example.BottomAppBarDemo()); + + // Trigger the SnackBar. + await tester.tap(find.byTooltip('Open popup menu')); + await tester.pump(); + + expect(find.text('Yay! A SnackBar!'), findsOneWidget); + + expect(find.text('Undo'), findsOneWidget); + }, + ); +} diff --git a/examples/api/test/material/date_picker/show_date_picker.1_test.dart b/examples/api/test/material/date_picker/show_date_picker.1_test.dart new file mode 100644 index 0000000000000..62741c919e19a --- /dev/null +++ b/examples/api/test/material/date_picker/show_date_picker.1_test.dart @@ -0,0 +1,47 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/date_picker/show_date_picker.1.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Can show date picker', (WidgetTester tester) async { + const String datePickerTitle = 'Select date'; + const String initialDate = 'Sun, Jul 25'; + + await tester.pumpWidget(const example.DatePickerApp()); + + // The date picker is not shown initially. + expect(find.text(datePickerTitle), findsNothing); + expect(find.text(initialDate), findsNothing); + + expect(find.text('No date selected'), findsOneWidget); + expect(find.byType(OutlinedButton), findsOneWidget); + + // Tap the button to show the date picker. + await tester.tap(find.byType(OutlinedButton)); + await tester.pumpAndSettle(); + + // The initial date is shown. + expect(find.text(datePickerTitle), findsOneWidget); + expect(find.text(initialDate), findsOneWidget); + + // Tap another date to select it. + await tester.tap(find.text('30')); + await tester.pumpAndSettle(); + + // The selected date is shown. + expect(find.text(datePickerTitle), findsOneWidget); + expect(find.text('Fri, Jul 30'), findsOneWidget); + + // Tap OK to confirm the selection and close the date picker. + await tester.tap(find.text('OK')); + await tester.pumpAndSettle(); + + // The date picker is closed and the selected date is shown. + expect(find.text(datePickerTitle), findsNothing); + expect(find.text('30/7/2021'), findsOneWidget); + }); +} diff --git a/examples/api/test/material/flexible_space_bar/flexible_space_bar.0_test.dart b/examples/api/test/material/flexible_space_bar/flexible_space_bar.0_test.dart new file mode 100644 index 0000000000000..82907a4d55006 --- /dev/null +++ b/examples/api/test/material/flexible_space_bar/flexible_space_bar.0_test.dart @@ -0,0 +1,42 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/flexible_space_bar/flexible_space_bar.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + // The app being tested loads images via HTTP which the test + // framework defeats by default. + setUpAll(() { + HttpOverrides.global = null; + }); + + testWidgets('The app bar stretches when over-scrolled', (WidgetTester tester) async { + await tester.pumpWidget( + const example.FlexibleSpaceBarExampleApp(), + ); + + expect(find.text('Flight Report'), findsOne); + + expect(find.widgetWithText(ListTile, 'Sunday'), findsOne); + expect(find.widgetWithText(ListTile, 'Monday'), findsOne); + expect(find.text('sunny, h: 80, l: 65'), findsExactly(2)); + expect(find.byIcon(Icons.wb_sunny), findsExactly(2)); + + final Finder appBarContainer = find.byType(Image); + final Size sizeBeforeScroll = tester.getSize(appBarContainer); + final Offset target = tester.getCenter(find.byType(ListTile).first); + final TestGesture gesture = await tester.startGesture(target); + await gesture.moveBy(const Offset(0.0, 100.0)); + await tester.pump(const Duration(milliseconds: 10)); + await gesture.up(); + final Size sizeAfterScroll = tester.getSize(appBarContainer); + + expect(sizeBeforeScroll.height, lessThan(sizeAfterScroll.height)); + // Verifies ScrollBehavior.dragDevices is correctly set. + }, variant: TargetPlatformVariant.all()); +} diff --git a/examples/api/test/material/navigation_rail/navigation_rail.extended_animation.0_test.dart b/examples/api/test/material/navigation_rail/navigation_rail.extended_animation.0_test.dart new file mode 100644 index 0000000000000..c0cf89412a0de --- /dev/null +++ b/examples/api/test/material/navigation_rail/navigation_rail.extended_animation.0_test.dart @@ -0,0 +1,65 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/navigation_rail/navigation_rail.extended_animation.0.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Navigation rail animates itself between the normal and extended state', + (WidgetTester tester) async { + await tester.pumpWidget( + const example.ExtendedAnimationExampleApp(), + ); + + expect(find.text('Tap on FloatingActionButton to expand'), findsOne); + expect(find.widgetWithIcon(FloatingActionButton, Icons.add), findsOne); + expect(find.byIcon(Icons.favorite), findsOne); + expect(find.text('First'), findsOne); + expect(find.byIcon(Icons.bookmark_border), findsOne); + expect(find.text('Second'), findsOne); + expect(find.byIcon(Icons.star_border), findsOne); + expect(find.text('First'), findsOne); + + // The navigation rail should be in the normal state. + expect( + tester.getCenter(find.byType(FloatingActionButton)), + offsetMoreOrLessEquals(const Offset(40, 36), epsilon: 0.1), + ); + expect(find.widgetWithText(FloatingActionButton, 'CREATE'), findsNothing); + + // Expand the navigation rail. + await tester.tap(find.byType(FloatingActionButton)); + await tester.pump(); + await tester.pump(kThemeAnimationDuration * 0.5); + expect( + tester.getCenter(find.byType(FloatingActionButton)), + offsetMoreOrLessEquals(const Offset(128.1, 36), epsilon: 0.1), + ); + + await tester.pump(kThemeAnimationDuration * 0.5); + expect( + tester.getCenter(find.byType(FloatingActionButton)), + offsetMoreOrLessEquals(const Offset(132, 36), epsilon: 0.1), + ); + expect(find.widgetWithText(FloatingActionButton, 'CREATE'), findsOne); + + // Collapse the navigation rail. + await tester.tap(find.byType(FloatingActionButton)); + await tester.pump(); + await tester.pump(kThemeAnimationDuration * 0.5); + expect( + tester.getCenter(find.byType(FloatingActionButton)), + offsetMoreOrLessEquals(const Offset(128.1, 36), epsilon: 0.1), + ); + + await tester.pump(kThemeAnimationDuration * 0.5); + expect( + tester.getCenter(find.byType(FloatingActionButton)), + offsetMoreOrLessEquals(const Offset(40, 36), epsilon: 0.1), + ); + expect(find.widgetWithText(FloatingActionButton, 'CREATE'), findsNothing); + }); +} diff --git a/examples/api/test/material/platform_menu_bar/platform_menu_bar.0_test.dart b/examples/api/test/material/platform_menu_bar/platform_menu_bar.0_test.dart new file mode 100644 index 0000000000000..10aa59d56552b --- /dev/null +++ b/examples/api/test/material/platform_menu_bar/platform_menu_bar.0_test.dart @@ -0,0 +1,124 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_api_samples/material/platform_menu_bar/platform_menu_bar.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late _FakeMenuChannel fakeMenuChannel; + late PlatformMenuDelegate originalDelegate; + late DefaultPlatformMenuDelegate delegate; + + setUp(() { + fakeMenuChannel = _FakeMenuChannel((MethodCall call) async {}); + delegate = DefaultPlatformMenuDelegate(channel: fakeMenuChannel); + originalDelegate = WidgetsBinding.instance.platformMenuDelegate; + WidgetsBinding.instance.platformMenuDelegate = delegate; + }); + + tearDown(() { + WidgetsBinding.instance.platformMenuDelegate = originalDelegate; + }); + + testWidgets('PlatformMenuBar creates a menu', (WidgetTester tester) async { + await tester.pumpWidget( + const example.ExampleApp(), + ); + + expect( + find.text('This space intentionally left blank.\nShow a message here using the menu.'), + findsOne, + ); + expect(find.byType(PlatformMenuBar), findsOne); + + expect( + fakeMenuChannel.outgoingCalls.last.method, + 'Menu.setMenus', + ); + expect( + fakeMenuChannel.outgoingCalls.last.arguments, + equals(const { + '0': >[ + { + 'id': 11, + 'label': 'Flutter API Sample', + 'enabled': true, + 'children': >[ + {'id': 2, 'label': 'About', 'enabled': true}, + {'id': 3, 'isDivider': true}, + { + 'id': 5, + 'label': 'Show Message', + 'enabled': true, + 'shortcutCharacter': 'm', + 'shortcutModifiers': 0, + }, + { + 'id': 8, + 'label': 'Messages', + 'enabled': true, + 'children': >[ + { + 'id': 6, + 'label': 'I am not throwing away my shot.', + 'enabled': true, + 'shortcutTrigger': 49, + 'shortcutModifiers': 1, + }, + { + 'id': 7, + 'label': "There's a million things I haven't done, but just you wait.", + 'enabled': true, + 'shortcutTrigger': 50, + 'shortcutModifiers': 1, + }, + ], + }, + {'id': 9, 'isDivider': true}, + {'id': 10, 'enabled': true, 'platformProvidedMenu': 1}, + ], + }, + ], + }), + ); + }, variant: const TargetPlatformVariant({TargetPlatform.macOS})); +} + +class _FakeMenuChannel implements MethodChannel { + _FakeMenuChannel(this.outgoing); + + Future Function(MethodCall) outgoing; + Future Function(MethodCall)? incoming; + + List outgoingCalls = []; + + @override + BinaryMessenger get binaryMessenger => throw UnimplementedError(); + + @override + MethodCodec get codec => const StandardMethodCodec(); + + @override + Future> invokeListMethod(String method, [dynamic arguments]) => throw UnimplementedError(); + + @override + Future> invokeMapMethod(String method, [dynamic arguments]) => throw UnimplementedError(); + + @override + Future invokeMethod(String method, [dynamic arguments]) async { + final MethodCall call = MethodCall(method, arguments); + outgoingCalls.add(call); + return await outgoing(call) as T; + } + + @override + String get name => 'flutter/menu'; + + @override + void setMethodCallHandler(Future Function(MethodCall call)? handler) => incoming = handler; +} diff --git a/examples/api/test/material/selectable_region/selectable_region.0_test.dart b/examples/api/test/material/selectable_region/selectable_region.0_test.dart new file mode 100644 index 0000000000000..c7e95a1b3b326 --- /dev/null +++ b/examples/api/test/material/selectable_region/selectable_region.0_test.dart @@ -0,0 +1,53 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_api_samples/material/selectable_region/selectable_region.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + Future sendKeyCombination( + WidgetTester tester, + LogicalKeyboardKey key, + ) async { + final LogicalKeyboardKey modifier = switch (defaultTargetPlatform) { + TargetPlatform.iOS || TargetPlatform.macOS => LogicalKeyboardKey.meta, + _ => LogicalKeyboardKey.control, + }; + await tester.sendKeyDownEvent(modifier); + await tester.sendKeyDownEvent(key); + await tester.sendKeyUpEvent(key); + await tester.sendKeyUpEvent(modifier); + await tester.pump(); + } + + testWidgets('The icon can be selected with the text', (WidgetTester tester) async { + String? clipboard; + tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async { + if (methodCall.method == 'Clipboard.setData') { + clipboard = (methodCall.arguments as Map)['text'] as String; + } + return null; + }); + addTearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null); + }); + + await tester.pumpWidget( + const example.SelectableRegionExampleApp(), + ); + + await tester.tap(find.byIcon(Icons.key)); // Focus the application. + await tester.pump(); + + // Keyboard select all. + await sendKeyCombination(tester,LogicalKeyboardKey.keyA); + // Keyboard copy. + await sendKeyCombination(tester,LogicalKeyboardKey.keyC); + + expect(clipboard, 'SelectableRegion SampleSelect this iconCustom Text'); + }, variant: TargetPlatformVariant.all()); +} diff --git a/examples/api/test/material/widget_state_input_border/widget_state_input_border.0_test.dart b/examples/api/test/material/widget_state_input_border/widget_state_input_border.0_test.dart new file mode 100644 index 0000000000000..313f086bbd6a2 --- /dev/null +++ b/examples/api/test/material/widget_state_input_border/widget_state_input_border.0_test.dart @@ -0,0 +1,49 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/widget_state_input_border/widget_state_input_border.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('InputBorder appearance matches configuration', (WidgetTester tester) async { + const WidgetStateInputBorder inputBorder = WidgetStateInputBorder.resolveWith( + example.WidgetStateInputBorderExample.veryCoolBorder, + ); + + void expectBorderToMatch(Set states) { + final RenderBox renderBox = tester.renderObject( + find.descendant( + of: find.byType(TextField), + matching: find.byType(CustomPaint), + ), + ); + + final BorderSide side = inputBorder.resolve(states).borderSide; + expect( + renderBox, + paints..line(color: side.color, strokeWidth: side.width), + ); + } + + await tester.pumpWidget( + const example.WidgetStateInputBorderExampleApp(), + ); + expectBorderToMatch(const {WidgetState.disabled}); + + await tester.tap(find.byType(FilterChip)); + await tester.pumpAndSettle(); + expectBorderToMatch(const {}); + + await tester.tap(find.byType(TextField)); + await tester.pumpAndSettle(); + expectBorderToMatch(const {WidgetState.focused}); + + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: tester.getCenter(find.byType(TextField))); + await tester.pumpAndSettle(); + expectBorderToMatch(const {WidgetState.focused, WidgetState.hovered}); + }); +} diff --git a/examples/api/test/painting/star_border/star_border.0_test.dart b/examples/api/test/painting/star_border/star_border.0_test.dart new file mode 100644 index 0000000000000..a95f8be4a6be5 --- /dev/null +++ b/examples/api/test/painting/star_border/star_border.0_test.dart @@ -0,0 +1,230 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; + +import 'package:flutter_api_samples/painting/star_border/star_border.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + Finder getStartBorderFinder(StarBorder shape) { + return find.byWidgetPredicate( + (Widget widget) => widget is example.ExampleBorder && widget.border == shape, + ); + } + + testWidgets('Initial content is visible', (WidgetTester tester) async { + await tester.pumpWidget( + const example.StarBorderApp(), + ); + + expect(find.widgetWithText(AppBar, 'StarBorder Example'), findsOne); + expect(find.text('Star'), findsOne); + expect(find.text('Polygon'), findsOne); + expect(find.byType(SelectableText), findsExactly(2)); + expect(find.byType(example.ExampleBorder), findsExactly(2)); + + expect(find.byType(Slider), findsExactly(6)); + expect(find.text('Point Rounding'), findsOne); + expect(find.text('Valley Rounding'), findsOne); + expect(find.text('Squash'), findsOne); + expect(find.text('Rotation'), findsOne); + expect(find.text('Points'), findsOne); + expect(find.widgetWithText(OutlinedButton, 'Nearest'), findsOne); + expect(find.text('Inner Radius'), findsOne); + expect(find.widgetWithText(ElevatedButton, 'Reset'), findsOne); + }); + + testWidgets('StartBorder uses the values from the sliders', (WidgetTester tester) async { + await tester.pumpWidget( + const example.StarBorderApp(), + ); + + expect(find.text('0.00'), findsExactly(4)); + expect(find.text('5.0'), findsOne); + expect(find.text('0.40'), findsOne); + expect( + getStartBorderFinder(const StarBorder( + side: BorderSide(), + // The default values of the example are the same as the default + // values of the constructor. + )), + findsOne, + ); + expect( + find.text(''' +Container( + decoration: ShapeDecoration( + shape: StarBorder( + points: 5.00, + rotation: 0.00, + innerRadiusRatio: 0.40, + pointRounding: 0.00, + valleyRounding: 0.00, + squash: 0.00, + ), + ), +);''' + ), + findsOne, + ); + + // Put all the sliders to the middle. + for (int i = 0; i < 6; i++) { + await tester.tap(find.byType(Slider).at(i)); + await tester.pump(); + } + + expect(find.text('0.50'), findsExactly(4)); + expect(find.text('11.5'), findsOne); + expect(find.text('180.00'), findsOne); + expect( + getStartBorderFinder(const StarBorder( + side: BorderSide(), + points: 11.5, + innerRadiusRatio: 0.5, + pointRounding: 0.5, + valleyRounding: 0.5, + rotation: 180, + squash: 0.5, + )), + findsOne, + ); + expect( + find.text(''' +Container( + decoration: ShapeDecoration( + shape: StarBorder( + points: 11.50, + rotation: 180.00, + innerRadiusRatio: 0.50, + pointRounding: 0.50, + valleyRounding: 0.50, + squash: 0.50, + ), + ), +);''' + ), + findsOne, + ); + }); + + testWidgets('StartBorder.polygon uses the values from the sliders', (WidgetTester tester) async { + await tester.pumpWidget( + const example.StarBorderApp(), + ); + + expect(find.text('0.00'), findsExactly(4)); + expect(find.text('5.0'), findsOne); + expect(find.text('0.40'), findsOne); + expect( + getStartBorderFinder(const StarBorder( + side: BorderSide(), + )), + findsOne, + ); + expect( + find.text(''' +Container( + decoration: ShapeDecoration( + shape: StarBorder( + points: 5.00, + rotation: 0.00, + innerRadiusRatio: 0.40, + pointRounding: 0.00, + valleyRounding: 0.00, + squash: 0.00, + ), + ), +);''' + ), + findsOne, + ); + + // Put all the sliders to the middle. + for (int i = 0; i < 6; i++) { + await tester.tap(find.byType(Slider).at(i)); + await tester.pump(); + } + + expect(find.text('0.50'), findsExactly(4)); + expect(find.text('11.5'), findsOne); + expect(find.text('180.00'), findsOne); + expect( + getStartBorderFinder(const StarBorder( + side: BorderSide(), + points: 11.5, + innerRadiusRatio: 0.5, + pointRounding: 0.5, + valleyRounding: 0.5, + rotation: 180, + squash: 0.5, + )), + findsOne, + ); + expect( + find.text(''' +Container( + decoration: ShapeDecoration( + shape: StarBorder( + points: 11.50, + rotation: 180.00, + innerRadiusRatio: 0.50, + pointRounding: 0.50, + valleyRounding: 0.50, + squash: 0.50, + ), + ), +);''' + ), + findsOne, + ); + }); + + testWidgets('The "Nearest" button rounds the number of points', (WidgetTester tester) async { + await tester.pumpWidget( + const example.StarBorderApp(), + ); + + expect(find.text('5.0'), findsOne); + + // Put the points slider to the middle. + await tester.tap(find.byType(Slider).at(4)); + await tester.pump(); + + expect(find.text('11.5'), findsOne); + + await tester.tap(find.widgetWithText(OutlinedButton, 'Nearest')); + await tester.pump(); + + expect(find.text('12.0'), findsOne); + }); + + testWidgets('The "Reset" button resets the parameters to the default values', (WidgetTester tester) async { + await tester.pumpWidget( + const example.StarBorderApp(), + ); + + expect(find.text('0.00'), findsExactly(4)); + expect(find.text('5.0'), findsOne); + expect(find.text('0.40'), findsOne); + + // Put all the sliders to the middle. + for (int i = 0; i < 6; i++) { + await tester.tap(find.byType(Slider).at(i)); + await tester.pump(); + } + + expect(find.text('0.50'), findsExactly(4)); + expect(find.text('11.5'), findsOne); + expect(find.text('180.00'), findsOne); + + await tester.tap(find.widgetWithText(ElevatedButton, 'Reset')); + await tester.pump(); + + expect(find.text('0.00'), findsExactly(4)); + expect(find.text('5.0'), findsOne); + expect(find.text('0.40'), findsOne); + }); +} diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml index 014308ff9e119..c882697c02db2 100644 --- a/examples/hello_world/pubspec.yaml +++ b/examples/hello_world/pubspec.yaml @@ -37,8 +37,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -72,4 +72,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/examples/image_list/pubspec.yaml b/examples/image_list/pubspec.yaml index 50a063a140592..3ade039c1f385 100644 --- a/examples/image_list/pubspec.yaml +++ b/examples/image_list/pubspec.yaml @@ -33,8 +33,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -57,4 +57,4 @@ flutter: assets: - images/coast.jpg -# PUBSPEC CHECKSUM: 2616 +# PUBSPEC CHECKSUM: 5218 diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml index f6d8368ab6a37..3e8f45ab5240f 100644 --- a/examples/layers/pubspec.yaml +++ b/examples/layers/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +39,4 @@ flutter: - services/data.json uses-material-design: true -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml index caf9a2c43fade..011605bd34fdb 100644 --- a/examples/platform_channel/pubspec.yaml +++ b/examples/platform_channel/pubspec.yaml @@ -37,8 +37,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -75,4 +75,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/examples/platform_channel_swift/pubspec.yaml b/examples/platform_channel_swift/pubspec.yaml index 47d07c5a98355..39d52664b0182 100644 --- a/examples/platform_channel_swift/pubspec.yaml +++ b/examples/platform_channel_swift/pubspec.yaml @@ -37,8 +37,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -75,4 +75,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 6c0b +# PUBSPEC CHECKSUM: 130d diff --git a/examples/splash/pubspec.yaml b/examples/splash/pubspec.yaml index 53171d4244935..64df2d259b460 100644 --- a/examples/splash/pubspec.yaml +++ b/examples/splash/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +34,4 @@ dev_dependencies: test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/examples/texture/lib/main.dart b/examples/texture/lib/main.dart index 2f0cc7eed357f..78e5fda6a6f0c 100644 --- a/examples/texture/lib/main.dart +++ b/examples/texture/lib/main.dart @@ -35,53 +35,54 @@ class _TexturePageState extends State { body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, + spacing: 10, children: [ FutureBuilder( future: textureId, builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - if (snapshot.data != null) { - return SizedBox( - width: textureWidth.toDouble(), - height: textureHeight.toDouble(), - child: Texture(textureId: snapshot.data!), - ); - } else { - return const Text('Error creating texture'); - } - } else { + if (!snapshot.hasData) { return const Text('Creating texture...'); } + + if (snapshot.data == null) { + return const Text('Error creating texture'); + } + + return SizedBox( + width: textureWidth.toDouble(), + height: textureHeight.toDouble(), + child: Texture(textureId: snapshot.data!), + ); }, ), - const SizedBox(height: 10), OutlinedButton( - child: const Text('Flutter Navy'), - onPressed: () => setColor(0x04, 0x2b, 0x59)), - const SizedBox(height: 10), + child: const Text('Flutter Navy'), + onPressed: () => setColor(0x04, 0x2b, 0x59), + ), OutlinedButton( - child: const Text('Flutter Blue'), - onPressed: () => setColor(0x05, 0x53, 0xb1)), - const SizedBox(height: 10), + child: const Text('Flutter Blue'), + onPressed: () => setColor(0x05, 0x53, 0xb1), + ), OutlinedButton( - child: const Text('Flutter Sky'), - onPressed: () => setColor(0x02, 0x7d, 0xfd)), - const SizedBox(height: 10), + child: const Text('Flutter Sky'), + onPressed: () => setColor(0x02, 0x7d, 0xfd), + ), OutlinedButton( - child: const Text('Red'), - onPressed: () => setColor(0xf2, 0x5d, 0x50)), - const SizedBox(height: 10), + child: const Text('Red'), + onPressed: () => setColor(0xf2, 0x5d, 0x50), + ), OutlinedButton( - child: const Text('Yellow'), - onPressed: () => setColor(0xff, 0xf2, 0x75)), - const SizedBox(height: 10), + child: const Text('Yellow'), + onPressed: () => setColor(0xff, 0xf2, 0x75), + ), OutlinedButton( - child: const Text('Purple'), - onPressed: () => setColor(0x62, 0x00, 0xee)), - const SizedBox(height: 10), + child: const Text('Purple'), + onPressed: () => setColor(0x62, 0x00, 0xee), + ), OutlinedButton( - child: const Text('Green'), - onPressed: () => setColor(0x1c, 0xda, 0xc5)), + child: const Text('Green'), + onPressed: () => setColor(0x1c, 0xda, 0xc5), + ), ], ), ), diff --git a/examples/texture/pubspec.yaml b/examples/texture/pubspec.yaml index 4103516421c54..9a4676da1c47f 100644 --- a/examples/texture/pubspec.yaml +++ b/examples/texture/pubspec.yaml @@ -35,8 +35,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -68,4 +68,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 64c3 +# PUBSPEC CHECKSUM: 8ac5 diff --git a/packages/flutter/lib/fix_data/fix_material/fix_theme_data.yaml b/packages/flutter/lib/fix_data/fix_material/fix_theme_data.yaml index 9629ee87fb3a1..4e751ecb9d50b 100644 --- a/packages/flutter/lib/fix_data/fix_material/fix_theme_data.yaml +++ b/packages/flutter/lib/fix_data/fix_material/fix_theme_data.yaml @@ -26,6 +26,79 @@ # * WidgetState: fix_widget_state.yaml version: 1 transforms: + # Changes made in https://github.com/flutter/flutter/pull/155072 + - title: "Migrate 'ThemeData.dialogBackgroundColor' to 'DialogThemeData.backgroundColor'" + date: 2024-09-12 + element: + uris: [ 'material.dart' ] + method: 'copyWith' + inClass: 'ThemeData' + oneOf: + - if: "dialogBackgroundColor != '' && dialogTheme != ''" + changes: + - kind: 'removeParameter' + name: 'dialogBackgroundColor' + - if: "dialogBackgroundColor != '' && dialogTheme == ''" + changes: + - kind: 'removeParameter' + name: 'dialogBackgroundColor' + - kind: 'addParameter' + index: 135 + name: 'dialogTheme' + style: optional_named + argumentValue: + expression: 'DialogThemeData(backgroundColor: {% dialogBackgroundColor %})' + requiredIf: "dialogBackgroundColor != ''" + variables: + dialogBackgroundColor: + kind: 'fragment' + value: 'arguments[dialogBackgroundColor]' + dialogTheme: + kind: 'fragment' + value: 'arguments[dialogTheme]' + + # Changes made in https://github.com/flutter/flutter/pull/155072 + - title: "Migrate 'ThemeData.dialogBackgroundColor' to 'DialogThemeData.backgroundColor'" + date: 2024-09-12 + element: + uris: [ 'material.dart' ] + constructor: '' + inClass: 'ThemeData' + oneOf: + - if: "dialogBackgroundColor != '' && dialogTheme != ''" + changes: + - kind: 'removeParameter' + name: 'dialogBackgroundColor' + - if: "dialogBackgroundColor != '' && dialogTheme == ''" + changes: + - kind: 'removeParameter' + name: 'dialogBackgroundColor' + - kind: 'addParameter' + index: 135 + name: 'dialogTheme' + style: optional_named + argumentValue: + expression: 'DialogThemeData(backgroundColor: {% dialogBackgroundColor %})' + requiredIf: "dialogBackgroundColor != ''" + variables: + dialogBackgroundColor: + kind: 'fragment' + value: 'arguments[dialogBackgroundColor]' + dialogTheme: + kind: 'fragment' + value: 'arguments[dialogTheme]' + + # Changes made in https://github.com/flutter/flutter/pull/145523 + - title: "Remove 'buttonBarTheme'" + date: 2024-04-28 + element: + uris: [ 'material.dart' ] + constructor: '' + inClass: 'ThemeData' + changes: + - kind: 'removeParameter' + name: 'buttonBarTheme' + # Changes made in https://github.com/flutter/flutter/pull/87281 - title: "Remove 'fixTextFieldOutlineLabel'" date: 2021-04-30 @@ -1698,15 +1771,4 @@ transforms: - kind: 'removeParameter' name: 'useMaterial3' - # Changes made in https://github.com/flutter/flutter/pull/145523 - - title: "Remove 'buttonBarTheme'" - date: 2024-04-28 - element: - uris: [ 'material.dart' ] - constructor: '' - inClass: 'ThemeData' - changes: - - kind: 'removeParameter' - name: 'buttonBarTheme' - # Before adding a new fix: read instructions at the top of this file. diff --git a/packages/flutter/lib/fix_data/fix_material/fix_widget_state.yaml b/packages/flutter/lib/fix_data/fix_material/fix_widget_state.yaml index 65343238b6b03..3a62acd6e1b91 100644 --- a/packages/flutter/lib/fix_data/fix_material/fix_widget_state.yaml +++ b/packages/flutter/lib/fix_data/fix_material/fix_widget_state.yaml @@ -26,6 +26,34 @@ # * ThemeDate: fix_theme_data.yaml version: 1 transforms: + # Changes made in https://github.com/flutter/flutter/pull/154972 + - title: "Replace with 'WidgetStateInputBorder'" + date: 2024-02-01 + element: + uris: [ 'material.dart' ] + constructor: 'resolveWith' + inClass: 'MaterialStateOutlineInputBorder' + changes: + - kind: 'replacedBy' + newElement: + uris: [ 'material.dart' ] + constructor: 'resolveWith' + inClass: 'WidgetStateInputBorder' + + # Changes made in https://github.com/flutter/flutter/pull/154972 + - title: "Replace with 'WidgetStateInputBorder'" + date: 2024-02-01 + element: + uris: [ 'material.dart' ] + constructor: 'resolveWith' + inClass: 'MaterialStateUnderlineInputBorder' + changes: + - kind: 'replacedBy' + newElement: + uris: [ 'material.dart' ] + constructor: 'resolveWith' + inClass: 'WidgetStateInputBorder' + # Changes made in https://github.com/flutter/flutter/pull/142151 - title: "Replace with 'WidgetState'" date: 2024-02-01 diff --git a/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml b/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml index 16b29e1d3314f..977148f5c5492 100644 --- a/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml +++ b/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml @@ -23,7 +23,7 @@ # * ListWheelScrollView: fix_list_wheel_scroll_view.yaml version: 1 transforms: - # Changes made in TBD + # Changes made in https://github.com/flutter/flutter/pull/139260 - title: "Migrate to focusNode.enclosingScope!" date: 2023-11-29 element: diff --git a/packages/flutter/lib/src/cupertino/date_picker.dart b/packages/flutter/lib/src/cupertino/date_picker.dart index 0e89098938055..ccb2b644ff3c5 100644 --- a/packages/flutter/lib/src/cupertino/date_picker.dart +++ b/packages/flutter/lib/src/cupertino/date_picker.dart @@ -301,43 +301,38 @@ class CupertinoDatePicker extends StatefulWidget { this.itemExtent = _kItemExtent, this.selectionOverlayBuilder, }) : initialDateTime = initialDateTime ?? DateTime.now(), - assert( - itemExtent > 0, - 'item extent should be greater than 0', - ), - assert( - minuteInterval > 0 && 60 % minuteInterval == 0, - 'minute interval is not a positive integer factor of 60', - ) { - assert( - mode != CupertinoDatePickerMode.dateAndTime || minimumDate == null || !this.initialDateTime.isBefore(minimumDate!), - 'initial date is before minimum date', - ); - assert( - mode != CupertinoDatePickerMode.dateAndTime || maximumDate == null || !this.initialDateTime.isAfter(maximumDate!), - 'initial date is after maximum date', - ); - assert( - (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || (minimumYear >= 1 && this.initialDateTime.year >= minimumYear), - 'initial year is not greater than minimum year, or minimum year is not positive', - ); - assert( - (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || maximumYear == null || this.initialDateTime.year <= maximumYear!, - 'initial year is not smaller than maximum year', - ); - assert( - (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || minimumDate == null || !minimumDate!.isAfter(this.initialDateTime), - 'initial date ${this.initialDateTime} is not greater than or equal to minimumDate $minimumDate', - ); - assert( - (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || maximumDate == null || !maximumDate!.isBefore(this.initialDateTime), - 'initial date ${this.initialDateTime} is not less than or equal to maximumDate $maximumDate', - ); - assert( - this.initialDateTime.minute % minuteInterval == 0, - 'initial minute is not divisible by minute interval', - ); - } + assert(itemExtent > 0, 'item extent should be greater than 0'), + assert(minuteInterval > 0 && 60 % minuteInterval == 0, 'minute interval is not a positive integer factor of 60'), + assert( + mode != CupertinoDatePickerMode.dateAndTime || minimumDate == null || !(initialDateTime ?? DateTime.now()).isBefore(minimumDate), + 'initial date is before minimum date', + ), + assert( + mode != CupertinoDatePickerMode.dateAndTime || maximumDate == null || !(initialDateTime ?? DateTime.now()).isAfter(maximumDate), + 'initial date is after maximum date', + ), + assert( + (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || (minimumYear >= 1 && (initialDateTime ?? DateTime.now()).year >= minimumYear), + 'initial year is not greater than minimum year, or minimum year is not positive', + ), + assert( + (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || maximumYear == null || (initialDateTime ?? DateTime.now()).year <= maximumYear, + 'initial year is not smaller than maximum year', + ), + assert( + (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || minimumDate == null || !minimumDate.isAfter(initialDateTime ?? DateTime.now()), + 'initial date ${initialDateTime ?? DateTime.now()} is not greater than or equal to minimumDate $minimumDate', + ), + assert( + (mode != CupertinoDatePickerMode.date && mode != CupertinoDatePickerMode.monthYear) || maximumDate == null || !maximumDate.isBefore(initialDateTime ?? DateTime.now()), + 'initial date ${initialDateTime ?? DateTime.now()} is not less than or equal to maximumDate $maximumDate', + ), + assert( + (mode == CupertinoDatePickerMode.date) || !showDayOfWeek, + 'showDayOfWeek is only supported in date mode', + ), + assert((initialDateTime ?? DateTime.now()).minute % minuteInterval == 0, 'initial minute is not divisible by minute interval'); + /// The mode of the date picker as one of [CupertinoDatePickerMode]. Defaults /// to [CupertinoDatePickerMode.dateAndTime]. Value cannot change after @@ -414,7 +409,9 @@ class CupertinoDatePicker extends StatefulWidget { /// Defaults to null, which disables background painting entirely. final Color? backgroundColor; - /// Whether to show day of week alongside day. Defaults to false. + /// Whether to show the day of week alongside the day in [CupertinoDatePickerMode.date] mode. + /// + /// Defaults to false. final bool showDayOfWeek; /// {@macro flutter.cupertino.picker.itemExtent} diff --git a/packages/flutter/lib/src/cupertino/route.dart b/packages/flutter/lib/src/cupertino/route.dart index 48d40976bd054..390bac8b41e35 100644 --- a/packages/flutter/lib/src/cupertino/route.dart +++ b/packages/flutter/lib/src/cupertino/route.dart @@ -437,12 +437,15 @@ class CupertinoPageTransition extends StatefulWidget { /// /// {@macro flutter.widgets.delegatedTransition} static Widget? delegatedTransition(BuildContext context, Animation animation, Animation secondaryAnimation, bool allowSnapshotting, Widget? child) { - final Animation delegatedPositionAnimation = - CurvedAnimation( + final CurvedAnimation animation = CurvedAnimation( parent: secondaryAnimation, curve: Curves.linearToEaseOut, reverseCurve: Curves.easeInToLinear, - ).drive(_kMiddleLeftTween); + ); + final Animation delegatedPositionAnimation = + animation.drive(_kMiddleLeftTween); + animation.dispose(); + assert(debugCheckHasDirectionality(context)); final TextDirection textDirection = Directionality.of(context); return SlideTransition( diff --git a/packages/flutter/lib/src/cupertino/segmented_control.dart b/packages/flutter/lib/src/cupertino/segmented_control.dart index ee9f4bfb40242..b956a336b3ffb 100644 --- a/packages/flutter/lib/src/cupertino/segmented_control.dart +++ b/packages/flutter/lib/src/cupertino/segmented_control.dart @@ -18,6 +18,9 @@ const EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(horizont // Minimum height of the segmented control. const double _kMinSegmentedControlHeight = 28.0; +// The default color used for the text of the disabled segment. +const Color _kDisableTextColor = Color.fromARGB(115, 122, 122, 122); + // The duration of the fade animation used to transition when a new widget // is selected. const Duration _kFadeDuration = Duration(milliseconds: 165); @@ -57,17 +60,26 @@ const Duration _kFadeDuration = Duration(milliseconds: 165); /// A segmented control may optionally be created with custom colors. The /// [unselectedColor], [selectedColor], [borderColor], and [pressedColor] /// arguments can be used to override the segmented control's colors from -/// [CupertinoTheme] defaults. +/// [CupertinoTheme] defaults. The [disabledColor] and [disabledTextColor] +/// set the background and text colors of the segment when it is disabled. +/// +/// The segmented control can be disabled by adding children to the [Set] of +/// [disabledChildren]. If the child is not present in the [Set], it is enabled +/// by default. /// /// {@tool dartpad} /// This example shows a [CupertinoSegmentedControl] with an enum type. /// /// The callback provided to [onValueChanged] should update the state of /// the parent [StatefulWidget] using the [State.setState] method, so that -/// the parent gets rebuilt; for example: +/// the parent gets rebuilt. +/// +/// This example also demonstrates how to use the [disabledChildren] property by +/// toggling each [Switch] to enable or disable the segments. /// /// ** See code in examples/api/lib/cupertino/segmented_control/cupertino_segmented_control.0.dart ** /// {@end-tool} +/// /// See also: /// /// * [CupertinoSegmentedControl], a segmented control widget in the style used @@ -98,7 +110,10 @@ class CupertinoSegmentedControl extends StatefulWidget { this.selectedColor, this.borderColor, this.pressedColor, + this.disabledColor, + this.disabledTextColor, this.padding, + this.disabledChildren = const {}, }) : assert(children.length >= 2), assert( groupValue == null || children.keys.any((T child) => child == groupValue), @@ -148,11 +163,26 @@ class CupertinoSegmentedControl extends StatefulWidget { /// Defaults to the selectedColor at 20% opacity if null. final Color? pressedColor; + /// The color used to fill the background of the segment when it is disabled. + /// + /// If null, this color will be 50% opacity of the [selectedColor] when + /// the segment is selected. If the segment is unselected, this color will be + /// set to [unselectedColor]. + final Color? disabledColor; + + /// The color used for the text of the segment when it is disabled. + final Color? disabledTextColor; + /// The CupertinoSegmentedControl will be placed inside this padding. /// /// Defaults to EdgeInsets.symmetric(horizontal: 16.0) final EdgeInsetsGeometry? padding; + /// The set of identifying keys that correspond to the segments that should be disabled. + /// + /// All segments are enabled by default. + final Set disabledChildren; + @override State> createState() => _SegmentedControlState(); } @@ -172,6 +202,9 @@ class _SegmentedControlState extends State extends State extends State extends State extends State extends State? inputFormatters; diff --git a/packages/flutter/lib/src/cupertino/theme.dart b/packages/flutter/lib/src/cupertino/theme.dart index 36315e297caa1..f271bd9060248 100644 --- a/packages/flutter/lib/src/cupertino/theme.dart +++ b/packages/flutter/lib/src/cupertino/theme.dart @@ -137,10 +137,10 @@ class CupertinoTheme extends StatelessWidget { } } -/// Provides a [CupertinoTheme] to all decendents. +/// Provides a [CupertinoTheme] to all descendents. class InheritedCupertinoTheme extends InheritedTheme { /// Creates an [InheritedTheme] that provides a [CupertinoTheme] to all - /// decendents. + /// descendents. const InheritedCupertinoTheme({ super.key, required this.theme, diff --git a/packages/flutter/lib/src/foundation/diagnostics.dart b/packages/flutter/lib/src/foundation/diagnostics.dart index 6847d1433fe6a..5d50c0ef22a15 100644 --- a/packages/flutter/lib/src/foundation/diagnostics.dart +++ b/packages/flutter/lib/src/foundation/diagnostics.dart @@ -1605,12 +1605,29 @@ abstract class DiagnosticsNode { /// by this method and interactive tree views in the Flutter IntelliJ /// plugin. @mustCallSuper - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { Map result = {}; assert(() { final bool hasChildren = getChildren().isNotEmpty; - result = { + final Map essentialDetails = { 'description': toDescription(), + 'shouldIndent': style != DiagnosticsTreeStyle.flat && + style != DiagnosticsTreeStyle.error, + ...delegate.additionalNodeProperties(this, fullDetails: fullDetails), + if (delegate.subtreeDepth > 0) + 'children': toJsonList( + delegate.filterChildren(getChildren(), this), + this, + delegate, + fullDetails: fullDetails, + ), + }; + + result = !fullDetails ? essentialDetails : { + ...essentialDetails, 'type': runtimeType.toString(), if (name != null) 'name': name, @@ -1634,18 +1651,12 @@ abstract class DiagnosticsNode { 'allowWrap': allowWrap, if (allowNameWrap) 'allowNameWrap': allowNameWrap, - ...delegate.additionalNodeProperties(this), if (delegate.includeProperties) 'properties': toJsonList( delegate.filterProperties(getProperties(), this), this, delegate, - ), - if (delegate.subtreeDepth > 0) - 'children': toJsonList( - delegate.filterChildren(getChildren(), this), - this, - delegate, + fullDetails: fullDetails, ), }; return true; @@ -1661,8 +1672,9 @@ abstract class DiagnosticsNode { static List> toJsonList( List? nodes, DiagnosticsNode? parent, - DiagnosticsSerializationDelegate delegate, - ) { + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { bool truncated = false; if (nodes == null) { return const >[]; @@ -1674,7 +1686,10 @@ abstract class DiagnosticsNode { truncated = true; } final List> json = nodes.map>((DiagnosticsNode node) { - return node.toJsonMap(delegate.delegateForNode(node)); + return node.toJsonMap( + delegate.delegateForNode(node), + fullDetails: fullDetails, + ); }).toList(); if (truncated) { json.last['truncated'] = true; @@ -1857,8 +1872,17 @@ class StringProperty extends DiagnosticsProperty { final bool quoted; @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } json['quoted'] = quoted; return json; } @@ -1913,8 +1937,18 @@ abstract class _NumProperty extends DiagnosticsProperty { }) : super.lazy(); @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } + if (unit != null) { json['unit'] = unit; } @@ -2097,8 +2131,17 @@ class FlagProperty extends DiagnosticsProperty { ); @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (ifTrue != null) { json['ifTrue'] = ifTrue; } @@ -2219,8 +2262,17 @@ class IterableProperty extends DiagnosticsProperty> { } @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (value != null) { json['values'] = value!.map((T value) => value.toString()).toList(); } @@ -2357,8 +2409,17 @@ class ObjectFlagProperty extends DiagnosticsProperty { } @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (ifPresent != null) { json['ifPresent'] = ifPresent; } @@ -2435,8 +2496,17 @@ class FlagsSummary extends DiagnosticsProperty> { } @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (value.isNotEmpty) { json['values'] = _formattedValues().toList(); } @@ -2555,7 +2625,10 @@ class DiagnosticsProperty extends DiagnosticsNode { final bool allowNameWrap; @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { final T? v = value; List>? properties; if (delegate.expandPropertyValues && delegate.includeProperties && v is Diagnosticable && getProperties().isEmpty) { @@ -2565,9 +2638,16 @@ class DiagnosticsProperty extends DiagnosticsNode { delegate.filterProperties(v.toDiagnosticsNode().getProperties(), this), this, delegate, + fullDetails: fullDetails, ); } - final Map json = super.toJsonMap(delegate); + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (properties != null) { json['properties'] = properties; } @@ -3503,7 +3583,10 @@ abstract class DiagnosticsSerializationDelegate { /// /// This method is called for every [DiagnosticsNode] that's included in /// the serialization. - Map additionalNodeProperties(DiagnosticsNode node); + Map additionalNodeProperties( + DiagnosticsNode node, { + bool fullDetails = true, + }); /// Filters the list of [DiagnosticsNode]s that will be included as children /// for the given `owner` node. @@ -3595,7 +3678,10 @@ class _DefaultDiagnosticsSerializationDelegate implements DiagnosticsSerializati }); @override - Map additionalNodeProperties(DiagnosticsNode node) { + Map additionalNodeProperties( + DiagnosticsNode node, { + bool fullDetails = true, + }) { return const {}; } diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 6cc016ca0f6af..251dd2dcff160 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -9,6 +9,7 @@ library; import 'dart:developer' show Flow, Timeline; import 'dart:io' show Platform; +import 'package:flutter/cupertino.dart' show CupertinoDialogAction; import 'package:flutter/foundation.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/widgets.dart' hide Flow; @@ -211,6 +212,62 @@ void showAboutDialog({ ); } +/// Displays either a Material or Cupertino [AboutDialog] depending on platform, +/// which describes the application and provides a button to show licenses +/// for software used by the application. +/// +/// The arguments correspond to the properties on [AboutDialog]. +/// +/// If the application has a [Drawer], consider using [AboutListTile] instead +/// of calling this directly. +/// +/// If you do not need an about box in your application, you should at least +/// provide an affordance to call [showLicensePage]. +/// +/// The licenses shown on the [LicensePage] are those returned by the +/// [LicenseRegistry] API, which can be used to add more licenses to the list. +/// +/// On most platforms this function will act the same as [showDialog], except +/// for iOS and macOS, in which case it will act the same as +/// [showCupertinoDialog]. +/// +/// The [context], [barrierDismissible], [barrierColor], [barrierLabel], +/// [useRootNavigator], [routeSettings] and [anchorPoint] arguments are +/// passed to [showAdaptiveDialog], the documentation for which discusses how it is used. +void showAdaptiveAboutDialog({ + required BuildContext context, + String? applicationName, + String? applicationVersion, + Widget? applicationIcon, + String? applicationLegalese, + List? children, + bool barrierDismissible = true, + Color? barrierColor, + String? barrierLabel, + bool useRootNavigator = true, + RouteSettings? routeSettings, + Offset? anchorPoint, +}) { + showAdaptiveDialog( + context: context, + barrierDismissible: barrierDismissible, + barrierColor: barrierColor, + barrierLabel: barrierLabel, + useRootNavigator: useRootNavigator, + builder: (BuildContext context) { + return AboutDialog.adaptive( + applicationName: applicationName, + applicationVersion: applicationVersion, + applicationIcon: applicationIcon, + applicationLegalese: applicationLegalese, + children: children, + ); + }, + routeSettings: routeSettings, + anchorPoint: anchorPoint, + ); +} + /// Displays a [LicensePage], which shows licenses for software used by the /// application. /// @@ -282,6 +339,28 @@ class AboutDialog extends StatelessWidget { this.children, }); + /// Creates an adaptive [AboutDialog] based on whether the target platform is + /// iOS or macOS, following Material design's + /// [Cross-platform guidelines](https://material.io/design/platform-guidance/cross-platform-adaptation.html). + /// + /// Typically passed as a child of [showAdaptiveAboutDialog], which will display + /// the [AboutDialog] differently based on platform. + /// + /// This constructor offers the same customization options as the default + /// [AboutDialog] constructor, allowing you to specify the application's + /// [applicationName], [applicationVersion], [applicationIcon], + /// [applicationLegalese], and additional [children] that appear in the dialog. + /// + /// The target platform is based on the current [Theme]: [ThemeData.platform]. + const factory AboutDialog.adaptive({ + Key? key, + String? applicationName, + String? applicationVersion, + Widget? applicationIcon, + String? applicationLegalese, + List? children, + }) = _AdaptiveAboutDialog; + /// The name of the application. /// /// Defaults to the value of [Title.title], if a [Title] widget can be found. @@ -384,6 +463,119 @@ class AboutDialog extends StatelessWidget { } } +class _AdaptiveAboutDialog extends AboutDialog { + const _AdaptiveAboutDialog({ + super.key, + super.applicationName, + super.applicationVersion, + super.applicationIcon, + super.applicationLegalese, + super.children, + }); + + List? _actions(BuildContext context) { + final ThemeData themeData = Theme.of(context); + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + + switch (themeData.platform) { + case TargetPlatform.iOS: + case TargetPlatform.macOS: + return [ + CupertinoDialogAction( + child: Text(themeData.useMaterial3 + ? localizations.viewLicensesButtonLabel + : localizations.viewLicensesButtonLabel.toUpperCase()), + onPressed: () { + showLicensePage( + context: context, + applicationName: applicationName, + applicationVersion: applicationVersion, + applicationIcon: applicationIcon, + applicationLegalese: applicationLegalese, + ); + }, + ), + CupertinoDialogAction( + child: Text(themeData.useMaterial3 + ? localizations.closeButtonLabel + : localizations.closeButtonLabel.toUpperCase()), + onPressed: () { + Navigator.pop(context); + }, + ), + ]; + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + return [ + TextButton( + child: Text(themeData.useMaterial3 + ? localizations.viewLicensesButtonLabel + : localizations.viewLicensesButtonLabel.toUpperCase()), + onPressed: () { + showLicensePage( + context: context, + applicationName: applicationName, + applicationVersion: applicationVersion, + applicationIcon: applicationIcon, + applicationLegalese: applicationLegalese, + ); + }, + ), + TextButton( + child: Text(themeData.useMaterial3 + ? localizations.closeButtonLabel + : localizations.closeButtonLabel.toUpperCase()), + onPressed: () { + Navigator.pop(context); + }, + ), + ]; + } + } + + @override + Widget build(BuildContext context) { + super.build(context); + + final String name = applicationName ?? _defaultApplicationName(context); + final String version = applicationVersion ?? _defaultApplicationVersion(context); + final Widget? icon = applicationIcon ?? _defaultApplicationIcon(context); + final ThemeData themeData = Theme.of(context); + final List? actions = _actions(context); + + return AlertDialog.adaptive( + content: ListBody( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (icon != null) IconTheme(data: themeData.iconTheme, child: icon), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + child: ListBody( + children: [ + Text(name, style: themeData.textTheme.headlineSmall), + Text(version, style: themeData.textTheme.bodyMedium), + const SizedBox(height: _textVerticalSeparation), + Text(applicationLegalese ?? '', style: themeData.textTheme.bodySmall), + ], + ), + ), + ), + ], + ), + ...?children, + ], + ), + actions: actions, + scrollable: true, + ); + } +} + /// A page that shows licenses for software used by the application. /// /// To show a [LicensePage], use [showLicensePage]. diff --git a/packages/flutter/lib/src/material/carousel.dart b/packages/flutter/lib/src/material/carousel.dart index 1e60e7e0ae256..55b957135cef9 100644 --- a/packages/flutter/lib/src/material/carousel.dart +++ b/packages/flutter/lib/src/material/carousel.dart @@ -646,8 +646,8 @@ class _RenderSliverFixedExtentCarousel extends RenderSliverFixedExtentBoxAdaptor if (index == firstVisibleIndex) { final double firstVisibleItemExtent = _buildItemExtent(index, _currentLayoutDimensions); - // If the first item is collapsed to be less than `effectievMinExtent`, - // then it should stop changinng its size and should start to scroll off screen. + // If the first item is collapsed to be less than `effectiveMinExtent`, + // then it should stop changing its size and should start to scroll off screen. if (firstVisibleItemExtent <= effectiveMinExtent) { return maxExtent * index - effectiveMinExtent + maxExtent; } diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index e1d02c6f23761..6366accd0dd21 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -54,7 +54,7 @@ const double _inputFormPortraitHeight = 98.0; const double _inputFormLandscapeHeight = 108.0; // 3.0 is the maximum scale factor on mobile phones. As of 07/30/24, iOS goes up -// to a max of 3.0 text sxale factor, and Android goes up to 2.0. This is the +// to a max of 3.0 text scale factor, and Android goes up to 2.0. This is the // default used for non-range date pickers. This default is changed to a lower // value at different parts of the date pickers depending on content, and device // orientation. @@ -156,6 +156,13 @@ const double _fontSizeToScale = 14.0; /// /// {@macro flutter.widgets.RawDialogRoute} /// +/// {@tool dartpad} +/// This sample demonstrates how to create a basic date picker. +/// Tapping the button displays a date picker which returns the selected date. +/// +/// ** See code in examples/api/lib/material/date_picker/show_date_picker.1.dart ** +/// {@end-tool} +/// /// ### State Restoration /// /// Using this method will not enable state restoration for the date picker. diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index c4200fc42c110..68d15ea69e2f2 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -93,8 +93,10 @@ class Dialog extends StatelessWidget { /// /// This sets the [Material.color] on this [Dialog]'s [Material]. /// - /// If `null`, [ColorScheme.surfaceContainerHigh] is used in Material 3. - /// Otherwise, defaults to [ThemeData.dialogBackgroundColor]. + /// If null, then the [DialogThemeData.backgroundColor] is used. If that is + /// also null, defaults to [ColorScheme.surfaceContainerHigh]. If + /// [ThemeData.useMaterial3] is false, defaults to [Colors.grey] with a shade + /// of 800 in dark theme and [Colors.white] in light theme. /// /// If [Dialog.fullscreen] is used, defaults to [ColorScheme.surface]. /// {@endtemplate} @@ -1638,9 +1640,7 @@ double _scalePadding(double textScaleFactor) { // Hand coded defaults based on Material Design 2. class _DialogDefaultsM2 extends DialogThemeData { _DialogDefaultsM2(this.context) - : _textTheme = Theme.of(context).textTheme, - _iconTheme = Theme.of(context).iconTheme, - super( + : super( alignment: Alignment.center, elevation: 24.0, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0))), @@ -1648,23 +1648,26 @@ class _DialogDefaultsM2 extends DialogThemeData { ); final BuildContext context; - final TextTheme _textTheme; - final IconThemeData _iconTheme; + late final ThemeData theme = Theme.of(context); + late final TextTheme textTheme = theme.textTheme; + late final IconThemeData iconTheme = theme.iconTheme; @override - Color? get iconColor => _iconTheme.color; + Color? get iconColor => iconTheme.color; @override - Color? get backgroundColor => Theme.of(context).dialogBackgroundColor; + Color? get backgroundColor => theme.brightness == Brightness.dark + ? Colors.grey[800]! + : Colors.white; @override - Color? get shadowColor => Theme.of(context).shadowColor; + Color? get shadowColor => theme.shadowColor; @override - TextStyle? get titleTextStyle => _textTheme.titleLarge; + TextStyle? get titleTextStyle => textTheme.titleLarge; @override - TextStyle? get contentTextStyle => _textTheme.titleMedium; + TextStyle? get contentTextStyle => textTheme.titleMedium; @override EdgeInsetsGeometry? get actionsPadding => EdgeInsets.zero; diff --git a/packages/flutter/lib/src/material/material_state.dart b/packages/flutter/lib/src/material/material_state.dart index 7f7c06885e1d3..3a54395115681 100644 --- a/packages/flutter/lib/src/material/material_state.dart +++ b/packages/flutter/lib/src/material/material_state.dart @@ -12,6 +12,7 @@ /// @docImport 'list_tile.dart'; /// @docImport 'outlined_button.dart'; /// @docImport 'text_button.dart'; +/// @docImport 'text_field.dart'; /// @docImport 'time_picker_theme.dart'; library; @@ -301,9 +302,19 @@ typedef MaterialStateTextStyle = WidgetStateTextStyle; /// [MaterialStateOutlineInputBorder] and override its [resolve] method. You'll also need /// to provide a `defaultValue` to the super constructor, so that we can know /// at compile-time what its default color is. +@Deprecated( + 'Use WidgetStateInputBorder instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' +) abstract class MaterialStateOutlineInputBorder extends OutlineInputBorder implements MaterialStateProperty { /// Abstract const constructor. This constructor enables subclasses to provide /// const constructors so that they can be used in const expressions. + @Deprecated( + 'Use WidgetStateInputBorder instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' + ) const MaterialStateOutlineInputBorder(); /// Creates a [MaterialStateOutlineInputBorder] from a [MaterialPropertyResolver] @@ -314,6 +325,11 @@ abstract class MaterialStateOutlineInputBorder extends OutlineInputBorder implem /// /// The given callback parameter must return a non-null text style in the default /// state. + @Deprecated( + 'Use WidgetStateInputBorder.resolveWith() instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' + ) const factory MaterialStateOutlineInputBorder.resolveWith(MaterialPropertyResolver callback) = _MaterialStateOutlineInputBorder; /// Returns a [InputBorder] that's to be used when a Material component is in the @@ -363,9 +379,19 @@ class _MaterialStateOutlineInputBorder extends MaterialStateOutlineInputBorder { /// [MaterialStateUnderlineInputBorder] and override its [resolve] method. You'll also need /// to provide a `defaultValue` to the super constructor, so that we can know /// at compile-time what its default color is. +@Deprecated( + 'Use WidgetStateInputBorder instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' +) abstract class MaterialStateUnderlineInputBorder extends UnderlineInputBorder implements MaterialStateProperty { /// Abstract const constructor. This constructor enables subclasses to provide /// const constructors so that they can be used in const expressions. + @Deprecated( + 'Use WidgetStateInputBorder instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' + ) const MaterialStateUnderlineInputBorder(); /// Creates a [MaterialStateUnderlineInputBorder] from a [MaterialPropertyResolver] @@ -376,6 +402,11 @@ abstract class MaterialStateUnderlineInputBorder extends UnderlineInputBorder im /// /// The given callback parameter must return a non-null text style in the default /// state. + @Deprecated( + 'Use WidgetStateInputBorder.resolveWith() instead. ' + 'Renamed to match other WidgetStateProperty objects. ' + 'This feature was deprecated after v3.26.0-0.1.pre.' + ) const factory MaterialStateUnderlineInputBorder.resolveWith(MaterialPropertyResolver callback) = _MaterialStateUnderlineInputBorder; /// Returns a [InputBorder] that's to be used when a Material component is in the @@ -400,6 +431,66 @@ class _MaterialStateUnderlineInputBorder extends MaterialStateUnderlineInputBord InputBorder resolve(Set states) => _resolve(states); } +/// Defines an [InputBorder] that is also a [WidgetStateProperty]. +/// +/// This class exists to enable widgets with [InputBorder] valued properties +/// to also accept [WidgetStateProperty] objects. +/// +/// [WidgetStateInputBorder] should only be used with widgets that document +/// their support, like [InputDecoration.border]. +/// +/// A [WidgetStateInputBorder] can be created by: +/// 1. Creating a class that extends [OutlineInputBorder] or [UnderlineInputBorder] +/// and implements [WidgetStateInputBorder]. The class would also need to +/// override the [resolve] method. +/// 2. Using [WidgetStateInputBorder.resolveWith] with a callback that +/// resolves the input border in the given states. +/// 3. Using [WidgetStateInputBorder.fromMap] to assign a border with a [WidgetStateMap]. +/// +/// {@tool dartpad} +/// This example shows how to use [WidgetStateInputBorder] to create +/// a [TextField] with an appearance that responds to user interaction. +/// +/// ** See code in examples/api/lib/material/widget_state_input_border/widget_state_input_border.0.dart ** +/// {@end-tool} +abstract interface class WidgetStateInputBorder implements InputBorder, WidgetStateProperty { + /// Creates a [WidgetStateInputBorder] using a [WidgetPropertyResolver] + /// callback. + /// + /// This constructor should only be used for fields that support + /// [WidgetStateInputBorder], such as [InputDecoration.border] + /// (if used as a regular [InputBorder], it acts the same as + /// an empty `OutlineInputBorder()` constructor). + const factory WidgetStateInputBorder.resolveWith( + WidgetPropertyResolver callback, + ) = _WidgetStateInputBorder; + + /// Creates a [WidgetStateOutlinedBorder] from a [WidgetStateMap]. + /// + /// {@macro flutter.widgets.WidgetStateProperty.fromMap} + /// It should only be used for fields that support [WidgetStateOutlinedBorder] + /// objects, such as [InputDecoration.border] + /// (throws an error if used as a regular [OutlinedBorder]). + /// + /// {@macro flutter.widgets.WidgetState.any} + const factory WidgetStateInputBorder.fromMap( + WidgetStateMap map, + ) = _WidgetInputBorderMapper; +} + +class _WidgetStateInputBorder extends OutlineInputBorder implements WidgetStateInputBorder { + const _WidgetStateInputBorder(this._resolve); + + final WidgetPropertyResolver _resolve; + + @override + InputBorder resolve(Set states) => _resolve(states); +} + +class _WidgetInputBorderMapper extends WidgetStateMapper implements WidgetStateInputBorder { + const _WidgetInputBorderMapper(super.map); +} + /// Interface for classes that [resolve] to a value of type `T` based /// on a widget's interactive "state", which is defined as a set /// of [MaterialState]s. diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index b29fb447ced7b..2f8cee2d507c5 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -650,8 +650,7 @@ class MenuController { /// Whether or not the associated menu is currently open. bool get isOpen { - assert(_anchor != null); - return _anchor!._isOpen; + return _anchor?._isOpen ?? false; } /// Close the menu that this menu controller is associated with. @@ -664,8 +663,7 @@ class MenuController { /// scrolled by an ancestor, or the view changes size, then any open menu will /// automatically close. void close() { - assert(_anchor != null); - _anchor!._close(); + _anchor?._close(); } /// Opens the menu that this menu controller is associated with. diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index 3155b377e3ee2..0cf8e3d323233 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -96,8 +96,6 @@ enum _ScaffoldSlot { /// ** See code in examples/api/lib/material/scaffold/scaffold_messenger.0.dart ** /// {@end-tool} /// -/// {@youtube 560 315 https://www.youtube.com/watch?v=lytQi-slT5Y} -/// /// See also: /// /// * [SnackBar], which is a temporary notification typically shown near the diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index d16a3369ff228..f6ab83a1426dc 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -159,6 +159,31 @@ class Tab extends StatelessWidget implements PreferredSizeWidget { /// If null, the height will be calculated based on the content of the [Tab]. When `icon` is not /// null along with `child` or `text`, the default height is 72.0 pixels. Without an `icon`, the /// height is 46.0 pixels. + /// + /// {@tool snippet} + /// + /// The provided tab height cannot be lower than the default height. Use + /// [PreferredSize] widget to adjust the overall [TabBar] height and match + /// the provided tab [height]: + /// + /// ```dart + /// bottom: const PreferredSize( + /// preferredSize: Size.fromHeight(20.0), + /// child: TabBar( + /// tabs: [ + /// Tab( + /// text: 'Tab 1', + /// height: 20.0, + /// ), + /// Tab( + /// text: 'Tab 2', + /// height: 20.0, + /// ), + /// ], + /// ), + /// ), + /// ``` + /// {@end-tool} final double? height; Widget _buildLabelText() { diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index 71a9d4294011f..2399626d0417e 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -292,6 +292,7 @@ class TextField extends StatefulWidget { this.onTap, this.onTapAlwaysCalled = false, this.onTapOutside, + this.onTapUpOutside, this.mouseCursor, this.buildCounter, this.scrollController, @@ -707,6 +708,9 @@ class TextField extends StatefulWidget { /// * [TapRegion] for how the region group is determined. final TapRegionCallback? onTapOutside; + /// {@macro flutter.widgets.editableText.onTapUpOutside} + final TapRegionUpCallback? onTapUpOutside; + /// The cursor for a mouse pointer when it enters or is hovering over the /// widget. /// @@ -1485,6 +1489,7 @@ class _TextFieldState extends State with RestorationMixin implements groupId: widget.groupId, onSelectionHandleTapped: _handleSelectionHandleTapped, onTapOutside: widget.onTapOutside, + onTapUpOutside: widget.onTapUpOutside, inputFormatters: formatters, rendererIgnoresPointer: true, mouseCursor: MouseCursor.defer, // TextField will handle the cursor diff --git a/packages/flutter/lib/src/material/text_form_field.dart b/packages/flutter/lib/src/material/text_form_field.dart index 519e71edbfe46..2e2760a7d8f0f 100644 --- a/packages/flutter/lib/src/material/text_form_field.dart +++ b/packages/flutter/lib/src/material/text_form_field.dart @@ -145,6 +145,7 @@ class TextFormField extends FormField { GestureTapCallback? onTap, bool onTapAlwaysCalled = false, TapRegionCallback? onTapOutside, + TapRegionUpCallback? onTapUpOutside, VoidCallback? onEditingComplete, ValueChanged? onFieldSubmitted, super.onSaved, @@ -245,6 +246,7 @@ class TextFormField extends FormField { onTap: onTap, onTapAlwaysCalled: onTapAlwaysCalled, onTapOutside: onTapOutside, + onTapUpOutside: onTapUpOutside, onEditingComplete: onEditingComplete, onSubmitted: onFieldSubmitted, inputFormatters: inputFormatters, diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart index 38b573c87c6a7..2d7043b1f494c 100644 --- a/packages/flutter/lib/src/material/theme_data.dart +++ b/packages/flutter/lib/src/material/theme_data.dart @@ -280,7 +280,6 @@ class ThemeData with Diagnosticable { // https://github.com/flutter/flutter/issues/91772. Color? canvasColor, Color? cardColor, - Color? dialogBackgroundColor, Color? disabledColor, Color? dividerColor, Color? focusColor, @@ -361,6 +360,11 @@ class ThemeData with Diagnosticable { 'This feature was deprecated after v3.21.0-10.0.pre.', ) ButtonBarThemeData? buttonBarTheme, + @Deprecated( + 'Use DialogThemeData.backgroundColor instead. ' + 'This feature was deprecated after v3.27.0-0.1.pre.', + ) + Color? dialogBackgroundColor, }) { // GENERAL CONFIGURATION cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault(); @@ -446,7 +450,6 @@ class ThemeData with Diagnosticable { unselectedWidgetColor ??= isDark ? Colors.white70 : Colors.black54; // Spec doesn't specify a dark theme secondaryHeaderColor, this is a guess. secondaryHeaderColor ??= isDark ? Colors.grey[700]! : primarySwatch[50]!; - dialogBackgroundColor ??= isDark ? Colors.grey[800]! : Colors.white; indicatorColor ??= colorScheme.secondary == primaryColor ? Colors.white : colorScheme.secondary; hintColor ??= isDark ? Colors.white60 : Colors.black.withOpacity(0.6); // The default [buttonTheme] is here because it doesn't use the defaults for @@ -558,6 +561,7 @@ class ThemeData with Diagnosticable { tooltipTheme ??= const TooltipThemeData(); // DEPRECATED (newest deprecations at the bottom) buttonBarTheme ??= const ButtonBarThemeData(); + dialogBackgroundColor ??= isDark ? Colors.grey[800]! : Colors.white; return ThemeData.raw( // For the sanity of the reader, make sure these properties are in the same // order in every place that they are separated by section comments (e.g. @@ -581,7 +585,6 @@ class ThemeData with Diagnosticable { canvasColor: canvasColor, cardColor: cardColor, colorScheme: colorScheme, - dialogBackgroundColor: dialogBackgroundColor, disabledColor: disabledColor, dividerColor: dividerColor, focusColor: focusColor, @@ -651,6 +654,7 @@ class ThemeData with Diagnosticable { tooltipTheme: tooltipTheme, // DEPRECATED (newest deprecations at the bottom) buttonBarTheme: buttonBarTheme, + dialogBackgroundColor: dialogBackgroundColor, ); } @@ -687,7 +691,6 @@ class ThemeData with Diagnosticable { // https://github.com/flutter/flutter/issues/91772. required this.canvasColor, required this.cardColor, - required this.dialogBackgroundColor, required this.disabledColor, required this.dividerColor, required this.focusColor, @@ -761,6 +764,11 @@ class ThemeData with Diagnosticable { 'This feature was deprecated after v3.21.0-10.0.pre.', ) ButtonBarThemeData? buttonBarTheme, + @Deprecated( + 'Use DialogThemeData.backgroundColor instead. ' + 'This feature was deprecated after v3.27.0-0.1.pre.', + ) + required this.dialogBackgroundColor, }) : // DEPRECATED (newest deprecations at the bottom) // should not be `required`, use getter pattern to avoid breakages. _buttonBarTheme = buttonBarTheme, @@ -1154,9 +1162,6 @@ class ThemeData with Diagnosticable { /// backwards compatibility breaks. final ColorScheme colorScheme; - /// The background color of [Dialog] elements. - final Color dialogBackgroundColor; - /// The color used for widgets that are inoperative, regardless of /// their state. For example, a disabled checkbox (which may be /// checked or unchecked). @@ -1424,6 +1429,13 @@ class ThemeData with Diagnosticable { ButtonBarThemeData get buttonBarTheme => _buttonBarTheme!; final ButtonBarThemeData? _buttonBarTheme; + /// The background color of [Dialog] elements. + @Deprecated( + 'Use DialogThemeData.backgroundColor instead. ' + 'This feature was deprecated after v3.27.0-0.1.pre.', + ) + final Color dialogBackgroundColor; + /// Creates a copy of this theme but with the given fields replaced with the new values. /// /// The [brightness] value is applied to the [colorScheme]. @@ -1453,7 +1465,6 @@ class ThemeData with Diagnosticable { // https://github.com/flutter/flutter/issues/91772. Color? canvasColor, Color? cardColor, - Color? dialogBackgroundColor, Color? disabledColor, Color? dividerColor, Color? focusColor, @@ -1537,6 +1548,11 @@ class ThemeData with Diagnosticable { 'This feature was deprecated after v3.21.0-10.0.pre.', ) ButtonBarThemeData? buttonBarTheme, + @Deprecated( + 'Use DialogThemeData.backgroundColor instead. ' + 'This feature was deprecated after v3.27.0-0.1.pre.', + ) + Color? dialogBackgroundColor, }) { cupertinoOverrideTheme = cupertinoOverrideTheme?.noDefault(); @@ -1591,7 +1607,6 @@ class ThemeData with Diagnosticable { canvasColor: canvasColor ?? this.canvasColor, cardColor: cardColor ?? this.cardColor, colorScheme: (colorScheme ?? this.colorScheme).copyWith(brightness: brightness), - dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor, disabledColor: disabledColor ?? this.disabledColor, dividerColor: dividerColor ?? this.dividerColor, focusColor: focusColor ?? this.focusColor, @@ -1659,7 +1674,9 @@ class ThemeData with Diagnosticable { timePickerTheme: timePickerTheme ?? this.timePickerTheme, toggleButtonsTheme: toggleButtonsTheme ?? this.toggleButtonsTheme, tooltipTheme: tooltipTheme ?? this.tooltipTheme, + // DEPRECATED (newest deprecations at the bottom) buttonBarTheme: buttonBarTheme ?? _buttonBarTheme, + dialogBackgroundColor: dialogBackgroundColor ?? this.dialogBackgroundColor, ); } @@ -1784,7 +1801,6 @@ class ThemeData with Diagnosticable { canvasColor: Color.lerp(a.canvasColor, b.canvasColor, t)!, cardColor: Color.lerp(a.cardColor, b.cardColor, t)!, colorScheme: ColorScheme.lerp(a.colorScheme, b.colorScheme, t), - dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t)!, disabledColor: Color.lerp(a.disabledColor, b.disabledColor, t)!, dividerColor: Color.lerp(a.dividerColor, b.dividerColor, t)!, focusColor: Color.lerp(a.focusColor, b.focusColor, t)!, @@ -1852,7 +1868,9 @@ class ThemeData with Diagnosticable { timePickerTheme: TimePickerThemeData.lerp(a.timePickerTheme, b.timePickerTheme, t), toggleButtonsTheme: ToggleButtonsThemeData.lerp(a.toggleButtonsTheme, b.toggleButtonsTheme, t)!, tooltipTheme: TooltipThemeData.lerp(a.tooltipTheme, b.tooltipTheme, t)!, + // DEPRECATED (newest deprecations at the bottom) buttonBarTheme: ButtonBarThemeData.lerp(a.buttonBarTheme, b.buttonBarTheme, t), + dialogBackgroundColor: Color.lerp(a.dialogBackgroundColor, b.dialogBackgroundColor, t)!, ); } @@ -1884,7 +1902,6 @@ class ThemeData with Diagnosticable { other.canvasColor == canvasColor && other.cardColor == cardColor && other.colorScheme == colorScheme && - other.dialogBackgroundColor == dialogBackgroundColor && other.disabledColor == disabledColor && other.dividerColor == dividerColor && other.focusColor == focusColor && @@ -1952,7 +1969,9 @@ class ThemeData with Diagnosticable { other.timePickerTheme == timePickerTheme && other.toggleButtonsTheme == toggleButtonsTheme && other.tooltipTheme == tooltipTheme && - other.buttonBarTheme == buttonBarTheme; + // DEPRECATED (newest deprecations at the bottom) + other.buttonBarTheme == buttonBarTheme && + other.dialogBackgroundColor == dialogBackgroundColor; } @override @@ -1982,7 +2001,6 @@ class ThemeData with Diagnosticable { canvasColor, cardColor, colorScheme, - dialogBackgroundColor, disabledColor, dividerColor, focusColor, @@ -2052,6 +2070,7 @@ class ThemeData with Diagnosticable { tooltipTheme, // DEPRECATED (newest deprecations at the bottom) buttonBarTheme, + dialogBackgroundColor, ]; return Object.hashAll(values); } @@ -2082,7 +2101,6 @@ class ThemeData with Diagnosticable { properties.add(ColorProperty('canvasColor', canvasColor, defaultValue: defaultData.canvasColor, level: DiagnosticLevel.debug)); properties.add(ColorProperty('cardColor', cardColor, defaultValue: defaultData.cardColor, level: DiagnosticLevel.debug)); properties.add(DiagnosticsProperty('colorScheme', colorScheme, defaultValue: defaultData.colorScheme, level: DiagnosticLevel.debug)); - properties.add(ColorProperty('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor, level: DiagnosticLevel.debug)); properties.add(ColorProperty('disabledColor', disabledColor, defaultValue: defaultData.disabledColor, level: DiagnosticLevel.debug)); properties.add(ColorProperty('dividerColor', dividerColor, defaultValue: defaultData.dividerColor, level: DiagnosticLevel.debug)); properties.add(ColorProperty('focusColor', focusColor, defaultValue: defaultData.focusColor, level: DiagnosticLevel.debug)); @@ -2152,6 +2170,7 @@ class ThemeData with Diagnosticable { properties.add(DiagnosticsProperty('tooltipTheme', tooltipTheme, level: DiagnosticLevel.debug)); // DEPRECATED (newest deprecations at the bottom) properties.add(DiagnosticsProperty('buttonBarTheme', buttonBarTheme, defaultValue: defaultData.buttonBarTheme, level: DiagnosticLevel.debug)); + properties.add(ColorProperty('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor, level: DiagnosticLevel.debug)); } } diff --git a/packages/flutter/lib/src/painting/box_border.dart b/packages/flutter/lib/src/painting/box_border.dart index ff7853c8b9e0d..f9cba2deea305 100644 --- a/packages/flutter/lib/src/painting/box_border.dart +++ b/packages/flutter/lib/src/painting/box_border.dart @@ -463,9 +463,6 @@ class Border extends BoxBorder { @override EdgeInsetsGeometry get dimensions { - if (_widthIsUniform) { - return EdgeInsets.all(top.strokeInset); - } return EdgeInsets.fromLTRB(left.strokeInset, top.strokeInset, right.strokeInset, bottom.strokeInset); } @@ -803,9 +800,6 @@ class BorderDirectional extends BoxBorder { @override EdgeInsetsGeometry get dimensions { - if (isUniform) { - return EdgeInsetsDirectional.all(top.strokeInset); - } return EdgeInsetsDirectional.fromSTEB(start.strokeInset, top.strokeInset, end.strokeInset, bottom.strokeInset); } diff --git a/packages/flutter/lib/src/painting/colors.dart b/packages/flutter/lib/src/painting/colors.dart index a897c0b33d660..b74862657687a 100644 --- a/packages/flutter/lib/src/painting/colors.dart +++ b/packages/flutter/lib/src/painting/colors.dart @@ -495,8 +495,17 @@ class ColorProperty extends DiagnosticsProperty { }); @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (value != null) { json['valueProperties'] = { 'red': value!.red, diff --git a/packages/flutter/lib/src/painting/text_painter.dart b/packages/flutter/lib/src/painting/text_painter.dart index a4774dcd92806..047de8e0bdf35 100644 --- a/packages/flutter/lib/src/painting/text_painter.dart +++ b/packages/flutter/lib/src/painting/text_painter.dart @@ -219,7 +219,7 @@ class WordBoundary extends TextBoundary { }; } - static final RegExp _regExpSpaceSeparatorOrPunctuaion = RegExp(r'[\p{Space_Separator}\p{Punctuation}]', unicode: true); + static final RegExp _regExpSpaceSeparatorOrPunctuation = RegExp(r'[\p{Space_Separator}\p{Punctuation}]', unicode: true); bool _skipSpacesAndPunctuations(int offset, bool forward) { // Use code point since some punctuations are supplementary characters. // "inner" here refers to the code unit that's before the break in the @@ -236,7 +236,7 @@ class WordBoundary extends TextBoundary { final bool hardBreakRulesApply = innerCodePoint == null || outerCodeUnit == null // WB3a & WB3b: always break before and after newlines. || _isNewline(innerCodePoint) || _isNewline(outerCodeUnit); - return hardBreakRulesApply || !_regExpSpaceSeparatorOrPunctuaion.hasMatch(String.fromCharCode(innerCodePoint)); + return hardBreakRulesApply || !_regExpSpaceSeparatorOrPunctuation.hasMatch(String.fromCharCode(innerCodePoint)); } /// Returns a [TextBoundary] suitable for handling keyboard navigation diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index beca5630d17fb..76640557ce3d1 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -972,7 +972,7 @@ extension type const BaselineOffset(double? offset) { /// /// When both `this` and `other` are [noBaseline], this method returns /// [noBaseline]. When one of them is [noBaseline], this method returns the - /// other oprand that's not [noBaseline]. + /// other operand that's not [noBaseline]. BaselineOffset minOf(BaselineOffset other) { return switch ((this, other)) { (final double lhs?, final double rhs?) => lhs >= rhs ? other : this, diff --git a/packages/flutter/lib/src/rendering/sliver_group.dart b/packages/flutter/lib/src/rendering/sliver_group.dart index b4edc7b0eb20a..339021edd9a15 100644 --- a/packages/flutter/lib/src/rendering/sliver_group.dart +++ b/packages/flutter/lib/src/rendering/sliver_group.dart @@ -6,6 +6,7 @@ library; import 'dart:math' as math; + import 'package:vector_math/vector_math_64.dart'; import 'object.dart'; @@ -213,7 +214,14 @@ class RenderSliverMainAxisGroup extends RenderSliver with ContainerRenderObjectM double childScrollOffset = 0.0; RenderSliver? current = childBefore(child as RenderSliver); while (current != null) { - childScrollOffset += current.geometry!.scrollExtent; + // If the current child is not the first child, then we need to + // add the scroll extent of the previous child to the current child's + // scroll offset. + if (childBefore(current) != null) { + childScrollOffset += childAfter(current)!.geometry!.scrollExtent + child.geometry!.scrollExtent; + } else if (!(childAfter(child) != null && current.geometry!.hasVisualOverflow)) { + childScrollOffset += current.geometry!.scrollExtent; + } current = childBefore(current); } return childScrollOffset; diff --git a/packages/flutter/lib/src/services/text_input.dart b/packages/flutter/lib/src/services/text_input.dart index 8c46bf88c0b8b..eb8d91a54266f 100644 --- a/packages/flutter/lib/src/services/text_input.dart +++ b/packages/flutter/lib/src/services/text_input.dart @@ -2576,7 +2576,7 @@ class SystemContextMenuController with SystemContextMenuClient { @override String toString() { - return 'SystemContextMenuController(onSystemHide=$onSystemHide, _hiddenBySystem=$_hiddenBySystem, _isVisible=$_isVisible, _isDiposed=$_isDisposed)'; + return 'SystemContextMenuController(onSystemHide=$onSystemHide, _hiddenBySystem=$_hiddenBySystem, _isVisible=$_isVisible, _isDisposed=$_isDisposed)'; } /// Used to release resources when this instance will never be used again. diff --git a/packages/flutter/lib/src/widgets/_html_element_view_io.dart b/packages/flutter/lib/src/widgets/_html_element_view_io.dart index e99944b92c64d..80fe7311c41e7 100644 --- a/packages/flutter/lib/src/widgets/_html_element_view_io.dart +++ b/packages/flutter/lib/src/widgets/_html_element_view_io.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:flutter/rendering.dart'; + import 'framework.dart'; import 'platform_view.dart'; @@ -14,6 +16,7 @@ extension HtmlElementViewImpl on HtmlElementView { required String tagName, bool isVisible = true, ElementCreatedCallback? onElementCreated, + required PlatformViewHitTestBehavior hitTestBehavior, }) { throw UnimplementedError('HtmlElementView is only available on Flutter Web'); } diff --git a/packages/flutter/lib/src/widgets/_html_element_view_web.dart b/packages/flutter/lib/src/widgets/_html_element_view_web.dart index fa7060e6adfe7..820e4b414bc62 100644 --- a/packages/flutter/lib/src/widgets/_html_element_view_web.dart +++ b/packages/flutter/lib/src/widgets/_html_element_view_web.dart @@ -21,12 +21,16 @@ extension HtmlElementViewImpl on HtmlElementView { required String tagName, bool isVisible = true, ElementCreatedCallback? onElementCreated, + required PlatformViewHitTestBehavior hitTestBehavior, }) { return HtmlElementView( key: key, - viewType: isVisible ? ui_web.PlatformViewRegistry.defaultVisibleViewType : ui_web.PlatformViewRegistry.defaultInvisibleViewType, + viewType: isVisible + ? ui_web.PlatformViewRegistry.defaultVisibleViewType + : ui_web.PlatformViewRegistry.defaultInvisibleViewType, onPlatformViewCreated: _createPlatformViewCallbackForElementCallback(onElementCreated), creationParams: {'tagName': tagName}, + hitTestBehavior: hitTestBehavior, ); } @@ -45,7 +49,7 @@ extension HtmlElementViewImpl on HtmlElementView { return PlatformViewSurface( controller: controller, gestureRecognizers: const >{}, - hitTestBehavior: PlatformViewHitTestBehavior.opaque, + hitTestBehavior: hitTestBehavior, ); }, ); diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 3eb41334e5470..7b2e53e11ba34 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -150,6 +150,22 @@ abstract class _UbiquitousInheritedWidget extends InheritedWidget { /// /// For example, [Padding] depends on the [Directionality] to resolve /// [EdgeInsetsDirectional] objects into absolute [EdgeInsets] objects. +/// +/// {@tool snippet} +/// +/// This example uses a right-to-left [TextDirection] and draws a blue box with +/// a right margin of 8 pixels. +/// +/// ```dart +/// Directionality( +/// textDirection: TextDirection.rtl, +/// child: Container( +/// margin: const EdgeInsetsDirectional.only(start: 8), +/// color: Colors.blue, +/// ), +/// ) +/// ``` +/// {@end-tool} class Directionality extends _UbiquitousInheritedWidget { /// Creates a widget that determines the directionality of text and /// text-direction-sensitive render objects. @@ -925,6 +941,17 @@ class ClipRRect extends SingleChildRenderObjectWidget { /// prevents its child from painting outside that oval, but the size and /// location of the clip oval can be customized using a custom [clipper]. /// +/// {@tool snippet} +/// +/// This example clips an image of a cat using an oval. +/// +/// ```dart +/// ClipOval( +/// child: Image.asset('images/cat.png'), +/// ) +/// ``` +/// {@end-tool} +/// /// See also: /// /// * [CustomClipper], for information about creating custom clips. diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index b5569aa391492..9ae6d0f6004eb 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -26,6 +26,7 @@ import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'actions.dart'; +import 'app_lifecycle_listener.dart'; import 'autofill.dart'; import 'automatic_keep_alive.dart'; import 'basic.dart'; @@ -850,6 +851,7 @@ class EditableText extends StatefulWidget { this.onSelectionHandleTapped, this.groupId = EditableText, this.onTapOutside, + this.onTapUpOutside, List? inputFormatters, this.mouseCursor, this.rendererIgnoresPointer = false, @@ -1492,8 +1494,8 @@ class EditableText extends StatefulWidget { final Object groupId; /// {@template flutter.widgets.editableText.onTapOutside} - /// Called for each tap that occurs outside of the[TextFieldTapRegion] group - /// when the text field is focused. + /// Called for each tap down that occurs outside of the [TextFieldTapRegion] + /// group when the text field is focused. /// /// If this is null, [EditableTextTapOutsideIntent] will be invoked. In the /// default implementation, [FocusNode.unfocus] will be called on the @@ -1530,10 +1532,27 @@ class EditableText extends StatefulWidget { /// See also: /// /// * [TapRegion] for how the region group is determined. + /// * [onTapUpOutside] which is called for each tap up. /// * [EditableTextTapOutsideIntent] for the intent that is invoked if /// this is null. final TapRegionCallback? onTapOutside; + /// {@template flutter.widgets.editableText.onTapUpOutside} + /// Called for each tap up that occurs outside of the [TextFieldTapRegion] + /// group when the text field is focused. + /// + /// The [PointerUpEvent] passed to the function is the event that caused the + /// notification. It is possible that the event may occur outside of the + /// immediate bounding box defined by the text field, although it will be + /// within the bounding box of a [TextFieldTapRegion] member. + /// {@endtemplate} + /// + /// See also: + /// + /// * [TapRegion] for how the region group is determined. + /// * [onTapOutside], which is called for each tap down. + final TapRegionUpCallback? onTapUpOutside; + /// {@template flutter.widgets.editableText.inputFormatters} /// Optional input validation and formatting overrides. /// @@ -2344,6 +2363,9 @@ class EditableTextState extends State with AutomaticKeepAliveClien Orientation? _lastOrientation; + late final AppLifecycleListener _appLifecycleListener; + bool _justResumed = false; + @override bool get wantKeepAlive => widget.focusNode.hasFocus; @@ -2966,6 +2988,9 @@ class EditableTextState extends State with AutomaticKeepAliveClien widget.focusNode.addListener(_handleFocusChanged); _cursorVisibilityNotifier.value = widget.showCursor; _spellCheckConfiguration = _inferSpellCheckConfiguration(widget.spellCheckConfiguration); + _appLifecycleListener = AppLifecycleListener( + onResume: () => _justResumed = true, + ); _initProcessTextActions(); } @@ -3180,6 +3205,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien clipboardStatus.removeListener(_onChangedClipboardStatus); clipboardStatus.dispose(); _cursorVisibilityNotifier.dispose(); + _appLifecycleListener.dispose(); FocusManager.instance.removeListener(_unflagInternalFocus); _disposeScrollNotificationObserver(); super.dispose(); @@ -4382,7 +4408,9 @@ class EditableTextState extends State with AutomaticKeepAliveClien final bool shouldSelectAll = widget.selectionEnabled && (kIsWeb || isDesktop) && !_isMultiline - && !_nextFocusChangeIsInternal; + && !_nextFocusChangeIsInternal + && !_justResumed; + _justResumed = false; if (shouldSelectAll) { // On native web and desktop platforms, single line tags // select all when receiving focus. @@ -5172,6 +5200,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien return TextFieldTapRegion( groupId: widget.groupId, onTapOutside: _hasFocus ? widget.onTapOutside ?? (PointerDownEvent event) => _defaultOnTapOutside(context, event) : null, + onTapUpOutside: widget.onTapUpOutside, debugLabel: kReleaseMode ? null : 'EditableText', child: MouseRegion( cursor: widget.mouseCursor ?? SystemMouseCursors.text, @@ -5328,10 +5357,8 @@ class EditableTextState extends State with AutomaticKeepAliveClien String text = _value.text; text = widget.obscuringCharacter * text.length; // Reveal the latest character in an obscured field only on mobile. - // Newer versions of iOS (iOS 15+) no longer reveal the most recently - // entered character. const Set mobilePlatforms = { - TargetPlatform.android, TargetPlatform.fuchsia, + TargetPlatform.android, TargetPlatform.fuchsia, TargetPlatform.iOS, }; final bool brieflyShowPassword = WidgetsBinding.instance.platformDispatcher.brieflyShowPassword && mobilePlatforms.contains(defaultTargetPlatform); diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index a1553a2ba121e..d62671075c211 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -5379,13 +5379,18 @@ class _ElementDiagnosticableTreeNode extends DiagnosticableTreeNode { final bool stateful; @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap(delegate, fullDetails: fullDetails,); final Element element = value as Element; if (!element.debugIsDefunct) { json['widgetRuntimeType'] = element.widget.runtimeType.toString(); } - json['stateful'] = stateful; + if (fullDetails) { + json['stateful'] = stateful; + } return json; } } diff --git a/packages/flutter/lib/src/widgets/icon_data.dart b/packages/flutter/lib/src/widgets/icon_data.dart index ca118126d1a5f..d13ce97863204 100644 --- a/packages/flutter/lib/src/widgets/icon_data.dart +++ b/packages/flutter/lib/src/widgets/icon_data.dart @@ -121,8 +121,17 @@ class IconDataProperty extends DiagnosticsProperty { }); @override - Map toJsonMap(DiagnosticsSerializationDelegate delegate) { - final Map json = super.toJsonMap(delegate); + Map toJsonMap( + DiagnosticsSerializationDelegate delegate, { + bool fullDetails = true, + }) { + final Map json = super.toJsonMap( + delegate, + fullDetails: fullDetails, + ); + if (!fullDetails) { + return json; + } if (value != null) { json['valueProperties'] = { 'codePoint': value!.codePoint, diff --git a/packages/flutter/lib/src/widgets/platform_view.dart b/packages/flutter/lib/src/widgets/platform_view.dart index 35e162fd13f25..5d0c5e234762c 100644 --- a/packages/flutter/lib/src/widgets/platform_view.dart +++ b/packages/flutter/lib/src/widgets/platform_view.dart @@ -655,6 +655,7 @@ class HtmlElementView extends StatelessWidget { required this.viewType, this.onPlatformViewCreated, this.creationParams, + this.hitTestBehavior = PlatformViewHitTestBehavior.opaque, }); /// Creates a platform view that creates a DOM element specified by [tagName]. @@ -672,12 +673,14 @@ class HtmlElementView extends StatelessWidget { required String tagName, bool isVisible = true, ElementCreatedCallback? onElementCreated, + PlatformViewHitTestBehavior hitTestBehavior = PlatformViewHitTestBehavior.opaque, }) => HtmlElementViewImpl.createFromTagName( key: key, tagName: tagName, isVisible: isVisible, onElementCreated: onElementCreated, + hitTestBehavior: hitTestBehavior, ); /// The unique identifier for the HTML view type to be embedded by this widget. @@ -695,6 +698,9 @@ class HtmlElementView extends StatelessWidget { /// Passed as the 2nd argument (i.e. `params`) of the registered view factory. final Object? creationParams; + /// {@macro flutter.widgets.AndroidView.hitTestBehavior} + final PlatformViewHitTestBehavior hitTestBehavior; + @override Widget build(BuildContext context) => buildImpl(context); } diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index 29f398356b17d..5cce284980ece 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -1476,12 +1476,12 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute '${Unicode.RLI}$tooltipAndLabel${Unicode.PDI}', TextDirection.ltr => tooltipAndLabel, }; } if (annotations.isEmpty) { - message = effectivelabel; + message = effectiveLabel; } else { - message = '$effectivelabel (${annotations.join('; ')})'; + message = '$effectiveLabel (${annotations.join('; ')})'; } } diff --git a/packages/flutter/lib/src/widgets/sliver_floating_header.dart b/packages/flutter/lib/src/widgets/sliver_floating_header.dart index 29f3a35aac243..bae405aff0acf 100644 --- a/packages/flutter/lib/src/widgets/sliver_floating_header.dart +++ b/packages/flutter/lib/src/widgets/sliver_floating_header.dart @@ -190,9 +190,9 @@ class _RenderSliverFloatingHeader extends RenderSliverSingleBoxAdapter { AnimationController? snapController; double? lastScrollOffset; - // The distance from the start of the header to the start of the viewport. Whent the + // The distance from the start of the header to the start of the viewport. When the // header is showing it varies between 0 (completely visible) and childExtent (not visible - // because it's just abopve the viewport's starting edge). It's used to compute the + // because it's just above the viewport's starting edge). It's used to compute the // header's paintExtent which defines where the header will appear - see paint(). late double effectiveScrollOffset; diff --git a/packages/flutter/lib/src/widgets/sliver_resizing_header.dart b/packages/flutter/lib/src/widgets/sliver_resizing_header.dart index b3e51a56d776b..9638cfcbc57d2 100644 --- a/packages/flutter/lib/src/widgets/sliver_resizing_header.dart +++ b/packages/flutter/lib/src/widgets/sliver_resizing_header.dart @@ -28,7 +28,7 @@ import 'slotted_render_object_widget.dart'; /// /// If the [minExtentPrototype] is null, then the default minimum extent is 0. If /// [maxExtentPrototype] is null then the default maximum extent is based on the child's -/// intrisic size. +/// intrinsic size. /// /// This sliver is preferable to the general purpose [SliverPersistentHeader] /// for its relatively narrow use case because there's no need to create a diff --git a/packages/flutter/lib/src/widgets/tap_region.dart b/packages/flutter/lib/src/widgets/tap_region.dart index 2436493422491..0b14093aac698 100644 --- a/packages/flutter/lib/src/widgets/tap_region.dart +++ b/packages/flutter/lib/src/widgets/tap_region.dart @@ -33,12 +33,24 @@ bool _tapRegionDebug(String message, [Iterable? details]) { return true; } -/// The type of callback that [TapRegion.onTapOutside] and -/// [TapRegion.onTapInside] take. +/// Signature for a callback called for a [PointerDownEvent] relative to a [TapRegion]. /// -/// The event is the pointer event that caused the callback to be called. +/// See also: +/// +/// * [TapRegion.onTapOutside], which is of this type. +/// * [TapRegion.onTapInside], which is of this type. +/// * [TapRegionUpCallback], which is similar but for [PointerUpEvent]s. typedef TapRegionCallback = void Function(PointerDownEvent event); +/// Signature for a callback called for a [PointerUpEvent] relative to a [TapRegion]. +/// +/// See also: +/// +/// * [TapRegion.onTapUpOutside], which is of this type. +/// * [TapRegion.onTapUpInside], which is of this type. +/// * [TapRegionCallback], which is similar but for [PointerDownEvent]s. +typedef TapRegionUpCallback = void Function(PointerUpEvent event); + /// An interface for registering and unregistering a [RenderTapRegion] /// (typically created with a [TapRegion] widget) with a /// [RenderTapRegionSurface] (typically created with a [TapRegionSurface] @@ -89,21 +101,23 @@ abstract class TapRegionRegistry { /// by assigning a [TapRegion.groupId], where all the regions with the same /// groupId act as if they were all one region. /// -/// When a tap outside of a registered region or region group is detected, its -/// [TapRegion.onTapOutside] callback is called. If the tap is outside one -/// member of a group, but inside another, no notification is made. +/// When a tap down or tap up outside of a registered region or region group is +/// detected, its [TapRegion.onTapOutside] or [TapRegion.onTapUpOutside] +/// callback is called, respectively. If the tap is outside one member of a +/// group, but inside another, no notification is made. /// -/// When a tap inside of a registered region or region group is detected, its -/// [TapRegion.onTapInside] callback is called. If the tap is inside one member -/// of a group, all members are notified. +/// When a tap down or tap up inside of a registered region or region group is +/// detected, its [TapRegion.onTapInside] or [TapRegion.onTapUpInside] +/// callback is called, respectively. If the tap is inside one member of a +/// group, all members are notified. /// /// The [TapRegionSurface] should be defined at the highest level needed to /// encompass the entire area where taps should be monitored. This is typically /// around the entire app. If the entire app isn't covered, then taps outside of -/// the [TapRegionSurface] will be ignored and no [TapRegion.onTapOutside] calls -/// will be made for those events. The [WidgetsApp], [MaterialApp] and -/// [CupertinoApp] automatically include a [TapRegionSurface] around their -/// entire app. +/// the [TapRegionSurface] will be ignored and no [TapRegion.onTapOutside] or +/// [TapRegion.onTapUpOutside] calls will be made for those events. The +/// [WidgetsApp], [MaterialApp] and [CupertinoApp] automatically include a +/// [TapRegionSurface] around their entire app. /// /// [TapRegionSurface] does not participate in the [gesture /// disambiguation](https://flutter.dev/to/gesture-disambiguation) @@ -152,21 +166,24 @@ class TapRegionSurface extends SingleChildRenderObjectWidget { /// group by assigning a [RenderTapRegion.groupId], where all the regions with /// the same groupId act as if they were all one region. /// -/// When a tap outside of a registered region or region group is detected, its -/// [TapRegion.onTapOutside] callback is called. If the tap is outside one -/// member of a group, but inside another, no notification is made. +/// When a tap down or tap up outside of a registered region or region group is +/// detected, its [TapRegion.onTapOutside] or [TapRegion.onTapUpOutside] +/// callback is called, respectively. If the tap is outside one member of a +/// group, but inside another, no notification is made. /// -/// When a tap inside of a registered region or region group is detected, its -/// [TapRegion.onTapInside] callback is called. If the tap is inside one member -/// of a group, all members are notified. +/// When a tap down or tap up inside of a registered region or region group is +/// detected, its [TapRegion.onTapInside] or [TapRegion.onTapUpInside] +/// callback is called, respectively. If the tap is inside one member of a +/// group, all members are notified. /// /// The [RenderTapRegionSurface] should be defined at the highest level needed /// to encompass the entire area where taps should be monitored. This is /// typically around the entire app. If the entire app isn't covered, then taps /// outside of the [RenderTapRegionSurface] will be ignored and no -/// [RenderTapRegion.onTapOutside] calls will be made for those events. The -/// [WidgetsApp], [MaterialApp] and [CupertinoApp] automatically include a -/// [RenderTapRegionSurface] around the entire app. +/// [RenderTapRegion.onTapOutside] or [RenderTapRegion.onTapUpOutside] calls +/// will be made for those events. The [WidgetsApp], [MaterialApp] and +/// [CupertinoApp] automatically include a [RenderTapRegionSurface] around the +/// entire app. /// /// [RenderTapRegionSurface] does not participate in the [gesture /// disambiguation](https://flutter.dev/to/gesture-disambiguation) @@ -242,7 +259,7 @@ class RenderTapRegionSurface extends RenderProxyBoxWithHitTestBehavior implement return true; }(), 'A RenderTapRegion was registered when it was disabled.'); - if (event is! PointerDownEvent) { + if (event is! PointerDownEvent && event is! PointerUpEvent) { return; } @@ -258,8 +275,8 @@ class RenderTapRegionSurface extends RenderProxyBoxWithHitTestBehavior implement return; } - // A child was hit, so we need to call onTapOutside for those regions or - // groups of regions that were not hit. + // A child was hit, so we need to call onTapOutside / onTapUpOutside for + // those regions or groups of regions that were not hit. final Set hitRegions = _getRegionsHit(_registeredRegions, result.path).cast().toSet(); assert(_tapRegionDebug('Tap event hit ${hitRegions.length} descendants.')); @@ -275,22 +292,33 @@ class RenderTapRegionSurface extends RenderProxyBoxWithHitTestBehavior implement bool consumeOutsideTaps = false; for (final RenderTapRegion region in outsideRegions) { - assert(_tapRegionDebug('Calling onTapOutside for $region')); + if (event is PointerDownEvent) { + assert(_tapRegionDebug('Calling onTapOutside for $region')); + region.onTapOutside?.call(event); + } else if (event is PointerUpEvent) { + assert(_tapRegionDebug('Calling onTapUpOutside for $region')); + region.onTapUpOutside?.call(event); + } + if (region.consumeOutsideTaps) { assert(_tapRegionDebug('Stopping tap propagation for $region (and all of ${region.groupId})')); consumeOutsideTaps = true; } - region.onTapOutside?.call(event); } for (final RenderTapRegion region in insideRegions) { - assert(_tapRegionDebug('Calling onTapInside for $region')); - region.onTapInside?.call(event); + if (event is PointerDownEvent) { + assert(_tapRegionDebug('Calling onTapInside for $region')); + region.onTapInside?.call(event); + } else if (event is PointerUpEvent) { + assert(_tapRegionDebug('Calling onTapUpInside for $region')); + region.onTapUpInside?.call(event); + } } // If any of the "outside" regions have consumeOutsideTaps set, then stop // the propagation of the event through the gesture recognizer by adding it // to the recognizer and immediately resolving it. - if (consumeOutsideTaps) { + if (consumeOutsideTaps && event is PointerDownEvent) { GestureBinding.instance.gestureArena.add(event.pointer, _DummyTapRecognizer()).resolve(GestureDisposition.accepted); } } @@ -332,7 +360,7 @@ class _DummyTapRecognizer extends GestureArenaMember { /// If there is no [TapRegionSurface] ancestor, [TapRegion] will do nothing. /// /// [TapRegion] is aware of the [Route]s in the [Navigator], so that [onTapOutside] -/// isn't called after the user navigates to a different page. +/// or [onTapUpOutside] isn't called after the user navigates to a different page. class TapRegion extends SingleChildRenderObjectWidget { /// Creates a const [TapRegion]. /// @@ -344,6 +372,8 @@ class TapRegion extends SingleChildRenderObjectWidget { this.behavior = HitTestBehavior.deferToChild, this.onTapOutside, this.onTapInside, + this.onTapUpOutside, + this.onTapUpInside, this.groupId, this.consumeOutsideTaps = false, String? debugLabel, @@ -360,37 +390,71 @@ class TapRegion extends SingleChildRenderObjectWidget { /// See [HitTestBehavior] for the allowed values and their meanings. final HitTestBehavior behavior; - /// A callback to be invoked when a tap is detected outside of this + /// A callback to be invoked when a tap down is detected outside of this /// [TapRegion] and any other region with the same [groupId], if any. /// /// The [PointerDownEvent] passed to the function is the event that caused the /// notification. If this region is part of a group (i.e. [groupId] is set), /// then it's possible that the event may be outside of this immediate region, /// although it will be within the region of one of the group members. + /// + /// See also: + /// * [onTapUpOutside], which is called when a tap up is detected outside + /// of this region. final TapRegionCallback? onTapOutside; - /// A callback to be invoked when a tap is detected inside of this + /// A callback to be invoked when a tap down is detected inside of this /// [TapRegion], or any other tap region with the same [groupId], if any. /// /// The [PointerDownEvent] passed to the function is the event that caused the /// notification. If this region is part of a group (i.e. [groupId] is set), /// then it's possible that the event may be outside of this immediate region, /// although it will be within the region of one of the group members. + /// + /// See also: + /// * [onTapUpInside], which is called when a tap up is detected inside + /// of this region. final TapRegionCallback? onTapInside; + /// A callback to be invoked when a tap up is detected outside of this + /// [TapRegion] and any other region with the same [groupId], if any. + /// + /// The [PointerUpEvent] passed to the function is the event that caused the + /// notification. If this region is part of a group (i.e. [groupId] is set), + /// then it's possible that the event may be outside of this immediate region, + /// although it will be within the region of one of the group members. + /// + /// See also: + /// * [onTapOutside], which is called when a tap down is detected outside + /// of this region. + final TapRegionUpCallback? onTapUpOutside; + + /// A callback to be invoked when a tap up is detected inside of this + /// [TapRegion], or any other tap region with the same [groupId], if any. + /// + /// The [PointerUpEvent] passed to the function is the event that caused the + /// notification. If this region is part of a group (i.e. [groupId] is set), + /// then it's possible that the event may be outside of this immediate region, + /// although it will be within the region of one of the group members. + /// + /// See also: + /// * [onTapInside], which is called when a tap down is detected inside + /// of this region. + final TapRegionUpCallback? onTapUpInside; + /// An optional group ID that groups [TapRegion]s together so that they /// operate as one region. If any member of a group is hit by a particular - /// tap, then the [onTapOutside] will not be called for any members of the - /// group. If any member of the group is hit, then all members will have their - /// [onTapInside] called. + /// tap, then the [onTapOutside] / [onTapUpOutside] will not be called for + /// any members of the group. If any member of the group is hit, then all + /// members will have their [onTapInside] / [onTapUpInside] called. /// /// If the group id is null, then only this region is hit tested. final Object? groupId; /// If true, then the group that this region belongs to will stop the - /// propagation of the tap down event in the gesture arena. + /// propagation of all events in the gesture arena. /// - /// This is useful if you want to block the tap down from being given to a + /// This is useful if you want to block events from being given to a /// [GestureDetector] when [onTapOutside] is called. /// /// If other [TapRegion]s with the same [groupId] have [consumeOutsideTaps] @@ -416,6 +480,8 @@ class TapRegion extends SingleChildRenderObjectWidget { behavior: behavior, onTapOutside: isCurrent ? onTapOutside : null, onTapInside: onTapInside, + onTapUpOutside: isCurrent ? onTapUpOutside : null, + onTapUpInside: onTapUpInside, groupId: groupId, debugLabel: debugLabel, ); @@ -431,7 +497,9 @@ class TapRegion extends SingleChildRenderObjectWidget { ..behavior = behavior ..groupId = groupId ..onTapOutside = isCurrent ? onTapOutside : null - ..onTapInside = onTapInside; + ..onTapInside = onTapInside + ..onTapUpOutside = isCurrent ? onTapUpOutside : null + ..onTapUpInside = onTapUpInside; if (!kReleaseMode) { renderObject.debugLabel = debugLabel; } @@ -480,6 +548,8 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { bool consumeOutsideTaps = false, this.onTapOutside, this.onTapInside, + this.onTapUpOutside, + this.onTapUpInside, super.behavior = HitTestBehavior.deferToChild, Object? groupId, String? debugLabel, @@ -491,7 +561,7 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { bool _isRegistered = false; - /// A callback to be invoked when a tap is detected outside of this + /// A callback to be invoked when a tap down is detected outside of this /// [RenderTapRegion] and any other region with the same [groupId], if any. /// /// The [PointerDownEvent] passed to the function is the event that caused the @@ -500,7 +570,7 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { /// although it will be within the region of one of the group members. TapRegionCallback? onTapOutside; - /// A callback to be invoked when a tap is detected inside of this + /// A callback to be invoked when a tap down is detected inside of this /// [RenderTapRegion], or any other tap region with the same [groupId], if any. /// /// The [PointerDownEvent] passed to the function is the event that caused the @@ -509,6 +579,24 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { /// although it will be within the region of one of the group members. TapRegionCallback? onTapInside; + /// A callback to be invoked when a tap up is detected outside of this + /// [RenderTapRegion] and any other region with the same [groupId], if any. + /// + /// The [PointerUpEvent] passed to the function is the event that caused the + /// notification. If this region is part of a group (i.e. [groupId] is set), + /// then it's possible that the event may be outside of this immediate region, + /// although it will be within the region of one of the group members. + TapRegionUpCallback? onTapUpOutside; + + /// A callback to be invoked when a tap up is detected inside of this + /// [RenderTapRegion], or any other tap region with the same [groupId], if any. + /// + /// The [PointerUpEvent] passed to the function is the event that caused the + /// notification. If this region is part of a group (i.e. [groupId] is set), + /// then it's possible that the event may be outside of this immediate region, + /// although it will be within the region of one of the group members. + TapRegionUpCallback? onTapUpInside; + /// A label used in debug builds. Will be null in release builds. String? debugLabel; @@ -522,8 +610,8 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { } } - /// Whether or not the tap down even that triggers a call to [onTapOutside] - /// will continue on to participate in the gesture arena. + /// Whether or not the tap event that triggers a call to [onTapOutside] + /// or [onTapUpOutside] will continue on to participate in the gesture arena. /// /// If any [RenderTapRegion] in the same group has [consumeOutsideTaps] set to /// true, then the tap down event will be consumed before other gesture @@ -539,9 +627,9 @@ class RenderTapRegion extends RenderProxyBoxWithHitTestBehavior { /// An optional group ID that groups [RenderTapRegion]s together so that they /// operate as one region. If any member of a group is hit by a particular - /// tap, then the [onTapOutside] will not be called for any members of the - /// group. If any member of the group is hit, then all members will have their - /// [onTapInside] called. + /// tap, then the [onTapOutside] / [onTapUpOutside] will not be called for + /// any members of the group. If any member of the group is hit, then all + /// members will have their [onTapInside] / [onTapUpInside] called. /// /// If the group id is null, then only this region is hit tested. Object? get groupId => _groupId; @@ -647,6 +735,8 @@ class TextFieldTapRegion extends TapRegion { super.enabled, super.onTapOutside, super.onTapInside, + super.onTapUpOutside, + super.onTapUpInside, super.consumeOutsideTaps, super.debugLabel, super.groupId = EditableText, diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index fbba170d4ad88..71c80b1236143 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -101,7 +101,7 @@ class DefaultTextStyle extends InheritedTheme { /// essentially what this constructor does). /// /// If a [textHeightBehavior] is provided, the existing configuration will be - /// replaced compeletely. To retain part of the original [textHeightBehavior], + /// replaced completely. To retain part of the original [textHeightBehavior], /// manually obtain the ambient [DefaultTextStyle] using [DefaultTextStyle.of]. static Widget merge({ Key? key, diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart index bfab9a50245a0..2ec03e3cc8091 100644 --- a/packages/flutter/lib/src/widgets/transitions.dart +++ b/packages/flutter/lib/src/widgets/transitions.dart @@ -145,7 +145,7 @@ class _AnimatedState extends State { /// Signature for a builder used to control a page's exit transition. /// /// When a new route enters the stack, the `animation` argument is typically -/// used to control the entery and exit transition of the topmost route. The exit +/// used to control the enter and exit transition of the topmost route. The exit /// transition of the route just below the new route is controlled with the /// `secondaryAnimation`, which also controls the transition of the old route /// when the topmost route is popped off the stack. diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index d233170fdccd6..072b94e9209f2 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -1729,9 +1729,11 @@ mixin WidgetInspectorService { Map? _nodeToJson( DiagnosticsNode? node, - InspectorSerializationDelegate delegate, + InspectorSerializationDelegate delegate, { + bool fullDetails = true, + } ) { - return node?.toJsonMap(delegate); + return node?.toJsonMap(delegate, fullDetails: fullDetails); } bool _isValueCreatedByLocalProject(Object? value) { @@ -1801,8 +1803,14 @@ mixin WidgetInspectorService { List nodes, InspectorSerializationDelegate delegate, { required DiagnosticsNode? parent, + bool fullDetails = true, }) { - return DiagnosticsNode.toJsonList(nodes, parent, delegate); + return DiagnosticsNode.toJsonList( + nodes, + parent, + delegate, + fullDetails: fullDetails, + ); } /// Returns a JSON representation of the properties of the [DiagnosticsNode] @@ -1976,11 +1984,14 @@ mixin WidgetInspectorService { final String groupName = parameters['groupName']!; final bool isSummaryTree = parameters['isSummaryTree'] == 'true'; final bool withPreviews = parameters['withPreviews'] == 'true'; + // If the "fullDetails" parameter is not provided, default to true. + final bool fullDetails = parameters['fullDetails'] != 'false'; final Map? result = _getRootWidgetTreeImpl( groupName: groupName, isSummaryTree: isSummaryTree, withPreviews: withPreviews, + fullDetails: fullDetails, ); return Future>.value({ @@ -1992,6 +2003,7 @@ mixin WidgetInspectorService { required String groupName, required bool isSummaryTree, required bool withPreviews, + bool fullDetails = true, Map? Function( DiagnosticsNode, InspectorSerializationDelegate)? addAdditionalPropertiesCallback, @@ -2032,6 +2044,7 @@ mixin WidgetInspectorService { ? combinedAddAdditionalPropertiesCallback : null, ), + fullDetails: fullDetails, ); } @@ -3788,19 +3801,24 @@ class InspectorSerializationDelegate implements DiagnosticsSerializationDelegate bool get _interactive => groupName != null; @override - Map additionalNodeProperties(DiagnosticsNode node) { + Map additionalNodeProperties( + DiagnosticsNode node, { + bool fullDetails = true, + }) { final Map result = {}; final Object? value = node.value; + if (summaryTree && fullDetails) { + result['summaryTree'] = true; + } if (_interactive) { result['valueId'] = service.toId(value, groupName!); } - if (summaryTree) { - result['summaryTree'] = true; - } final _Location? creationLocation = _getCreationLocation(value); if (creationLocation != null) { - result['locationId'] = _toLocationId(creationLocation); - result['creationLocation'] = creationLocation.toJsonMap(); + if (fullDetails) { + result['locationId'] = _toLocationId(creationLocation); + result['creationLocation'] = creationLocation.toJsonMap(); + } if (service._isLocalCreationLocation(creationLocation.file)) { _nodesCreatedByLocalProject.add(node); result['createdByLocalProject'] = true; diff --git a/packages/flutter/lib/src/widgets/widget_state.dart b/packages/flutter/lib/src/widgets/widget_state.dart index bf9ff2577661c..6b482b666dc65 100644 --- a/packages/flutter/lib/src/widgets/widget_state.dart +++ b/packages/flutter/lib/src/widgets/widget_state.dart @@ -235,7 +235,7 @@ enum WidgetState implements WidgetStatesConstraint { /// {@template flutter.widgets.WidgetState.any} /// To prevent a situation where each [WidgetStatesConstraint] - /// isn't satisfied by the given set of states, consier adding + /// isn't satisfied by the given set of states, consider adding /// [WidgetState.any] as the final [WidgetStateMap] key. /// {@endtemplate} static const WidgetStatesConstraint any = _AnyWidgetStates(); diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index d2528d1396137..891f20694c4d9 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -29,9 +29,9 @@ dev_dependencies: sdk: flutter fake_async: 1.3.1 # To track memory leaks. - leak_tracker_flutter_testing: 3.0.8 + leak_tracker_flutter_testing: 3.0.9 leak_tracker_testing: 3.0.1 - leak_tracker: 10.0.7 + leak_tracker: 10.0.8 web: 1.1.0 async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -55,4 +55,4 @@ dev_dependencies: vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 25d2 +# PUBSPEC CHECKSUM: b2d4 diff --git a/packages/flutter/test/cupertino/date_picker_test.dart b/packages/flutter/test/cupertino/date_picker_test.dart index 7a751bf1718be..a9a9ad1dcb87a 100644 --- a/packages/flutter/test/cupertino/date_picker_test.dart +++ b/packages/flutter/test/cupertino/date_picker_test.dart @@ -261,6 +261,55 @@ void main() { }); }); + testWidgets('showDayOfWeek is only supported in date mode', (WidgetTester tester) async { + expect( + () => CupertinoDatePicker( + mode: CupertinoDatePickerMode.date, + onDateTimeChanged: (DateTime _) {}, + showDayOfWeek: true, + ), + returnsNormally, + ); + + expect( + () => CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + onDateTimeChanged: (DateTime _) {}, + showDayOfWeek: true, + ), + throwsA(isA().having( + (AssertionError e) => e.message ?? 'Unknown error', + 'message', + contains('showDayOfWeek is only supported in date mode'), + )), + ); + + expect( + () => CupertinoDatePicker( + mode: CupertinoDatePickerMode.monthYear, + onDateTimeChanged: (DateTime _) {}, + showDayOfWeek: true, + ), + throwsA(isA().having( + (AssertionError e) => e.message ?? 'Unknown error', + 'message', + contains('showDayOfWeek is only supported in date mode'), + )), + ); + + expect( + () => CupertinoDatePicker( + onDateTimeChanged: (DateTime _) {}, + showDayOfWeek: true, + ), + throwsA(isA().having( + (AssertionError e) => e.message ?? 'Unknown error', + 'message', + contains('showDayOfWeek is only supported in date mode'), + )), + ); + }); + testWidgets('picker honors minuteInterval and secondInterval', (WidgetTester tester) async { late Duration duration; await tester.pumpWidget( diff --git a/packages/flutter/test/cupertino/nav_bar_test.dart b/packages/flutter/test/cupertino/nav_bar_test.dart index 08e7d6d6d5b6d..61b5a5ec07889 100644 --- a/packages/flutter/test/cupertino/nav_bar_test.dart +++ b/packages/flutter/test/cupertino/nav_bar_test.dart @@ -1152,13 +1152,13 @@ void main() { scrollController.jumpTo(100.0); await tester.pump(); - final DecoratedBox decoratedBoxAfterSroll = tester.widgetList(find.descendant( + final DecoratedBox decoratedBoxAfterScroll = tester.widgetList(find.descendant( of: find.byType(CupertinoNavigationBar), matching: find.byType(DecoratedBox), )).first as DecoratedBox; - expect(decoratedBoxAfterSroll.decoration.runtimeType, BoxDecoration); + expect(decoratedBoxAfterScroll.decoration.runtimeType, BoxDecoration); - final BorderSide borderAfterScroll = (decoratedBoxAfterSroll.decoration as BoxDecoration).border!.bottom; + final BorderSide borderAfterScroll = (decoratedBoxAfterScroll.decoration as BoxDecoration).border!.bottom; expect(borderAfterScroll.color.opacity, 1.0); @@ -1325,13 +1325,13 @@ void main() { scrollController.jumpTo(400.0); await tester.pump(); - final DecoratedBox decoratedBoxAfterSroll = tester.widgetList(find.descendant( + final DecoratedBox decoratedBoxAfterScroll = tester.widgetList(find.descendant( of: find.byType(CupertinoSliverNavigationBar), matching: find.byType(DecoratedBox), )).first as DecoratedBox; - expect(decoratedBoxAfterSroll.decoration.runtimeType, BoxDecoration); + expect(decoratedBoxAfterScroll.decoration.runtimeType, BoxDecoration); - final BorderSide borderAfterScroll = (decoratedBoxAfterSroll.decoration as BoxDecoration).border!.bottom; + final BorderSide borderAfterScroll = (decoratedBoxAfterScroll.decoration as BoxDecoration).border!.bottom; expect(borderAfterScroll.color.opacity, 1.0); @@ -2089,6 +2089,7 @@ void main() { testWidgets('Large title snaps up to persistent nav bar when partially scrolled over halfway up', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); const double largeTitleHeight = 52.0; await tester.pumpWidget( @@ -2130,6 +2131,7 @@ void main() { testWidgets('Large title snaps back to extended height when partially scrolled halfway up or less', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); const double largeTitleHeight = 52.0; await tester.pumpWidget( @@ -2170,6 +2172,7 @@ void main() { testWidgets('Large title and bottom snap up when partially scrolled over halfway up in automatic mode', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); const double largeTitleHeight = 52.0; const double bottomHeight = 100.0; @@ -2228,6 +2231,7 @@ void main() { testWidgets('Large title and bottom snap down when partially scrolled halfway up or less in automatic mode', (WidgetTester tester) async { final ScrollController scrollController = ScrollController(); + addTearDown(scrollController.dispose); const double largeTitleHeight = 52.0; const double bottomHeight = 100.0; diff --git a/packages/flutter/test/cupertino/segmented_control_test.dart b/packages/flutter/test/cupertino/segmented_control_test.dart index b19c9590f0500..d763078316809 100644 --- a/packages/flutter/test/cupertino/segmented_control_test.dart +++ b/packages/flutter/test/cupertino/segmented_control_test.dart @@ -1641,4 +1641,123 @@ void main() { kIsWeb ? SystemMouseCursors.click : SystemMouseCursors.basic, ); }); + + testWidgets('Tap on disabled segment should not change its state', (WidgetTester tester) async { + final Map children = { + 0: const Text('Child 1'), + 1: const Text('Child 2'), + 2: const Text('Child 3'), + }; + + final Set disabledChildren = {1}; + + int sharedValue = 0; + + await tester.pumpWidget( + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return boilerplate( + child: CupertinoSegmentedControl( + key: const ValueKey('Segmented Control'), + children: children, + disabledChildren: disabledChildren, + onValueChanged: (int newValue) { + setState(() { + sharedValue = newValue; + }); + }, + groupValue: sharedValue, + ), + ); + }, + ), + ); + + expect(sharedValue, 0); + + await tester.tap(find.text('Child 2')); + await tester.pumpAndSettle(); + + expect(sharedValue, 0); + }); + + testWidgets('Background color of disabled segment should be different than enabled segment', (WidgetTester tester) async { + final Map children = { + 0: const Text('Child 1'), + 1: const Text('Child 2'), + }; + int sharedValue = 0; + + await tester.pumpWidget( + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return boilerplate( + child: CupertinoSegmentedControl( + children: children, + disabledChildren: const {0}, + onValueChanged: (int newValue) { + setState(() { + sharedValue = newValue; + }); + }, + groupValue: sharedValue, + ), + ); + }, + ), + ); + + // Colors are different for disabled and enabled segments in initial state. + // By default, the first segment is selected (and also is disabled in this test), + // it should have a blue background (selected color) with 50% opacity + expect( + getBackgroundColor(tester, 0), + isSameColorAs(CupertinoColors.systemBlue.withOpacity(0.5)), + ); + expect(getBackgroundColor(tester, 1), isSameColorAs(CupertinoColors.white)); + + // Tap on disabled segment should not change its color + await tester.tap(find.text('Child 1')); + await tester.pumpAndSettle(); + + expect( + getBackgroundColor(tester, 0), + isSameColorAs(CupertinoColors.systemBlue.withOpacity(0.5)), + ); + + // When tapping on another enabled segment, the first disabled segment is not selected anymore, + // it should have a white background (same to unselected color). + await tester.tap(find.text('Child 2')); + await tester.pumpAndSettle(); + + expect(getBackgroundColor(tester, 0), isSameColorAs(CupertinoColors.white)); + }); + + testWidgets('Custom disabled color of disabled segment is showing as desired', (WidgetTester tester) async { + final Map children = { + 0: const Text('Child 1'), + 1: const Text('Child 2'), + 2: const Text('Child 3'), + }; + + await tester.pumpWidget( + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return boilerplate( + child: CupertinoSegmentedControl( + children: children, + disabledChildren: const {0}, + onValueChanged: (int newValue) {}, + disabledColor: CupertinoColors.systemGrey2, + ), + ); + }, + ), + ); + + expect( + getBackgroundColor(tester, 0), + isSameColorAs(CupertinoColors.systemGrey2), + ); + }); } diff --git a/packages/flutter/test/cupertino/sliding_segmented_control_test.dart b/packages/flutter/test/cupertino/sliding_segmented_control_test.dart index 98a02195602d1..ba558087ba309 100644 --- a/packages/flutter/test/cupertino/sliding_segmented_control_test.dart +++ b/packages/flutter/test/cupertino/sliding_segmented_control_test.dart @@ -1058,7 +1058,7 @@ void main() { expect(groupValue, 1); }); - testWidgets('Non-centered taps work on propotional segments', (WidgetTester tester) async { + testWidgets('Non-centered taps work on proportional segments', (WidgetTester tester) async { final Map children = {}; children[0] = const SizedBox(width: 50, height: 30); children[1] = const SizedBox(); diff --git a/packages/flutter/test/foundation/diagnostics_json_test.dart b/packages/flutter/test/foundation/diagnostics_json_test.dart index 53bad0e833d91..867605d62c67d 100644 --- a/packages/flutter/test/foundation/diagnostics_json_test.dart +++ b/packages/flutter/test/foundation/diagnostics_json_test.dart @@ -24,6 +24,16 @@ void main() { }); group('Serialization', () { + const List essentialDiagnosticKeys = [ + 'description', + 'shouldIndent', + ]; + const List detailedDiagnosticKeys = [ + 'type', + 'hasChildren', + 'allowWrap', + ]; + final TestTree testTree = TestTree( properties: [ StringProperty('stringProperty1', 'value1', quoted: false), @@ -70,6 +80,46 @@ void main() { final Map result = testTree.toDiagnosticsNode().toJsonMap(const DiagnosticsSerializationDelegate()); expect(result.containsKey('properties'), isFalse); expect(result.containsKey('children'), isFalse); + + for (final String keyName in essentialDiagnosticKeys) { + expect( + result.containsKey(keyName), + isTrue, + reason: '$keyName is included.', + ); + } + for (final String keyName in detailedDiagnosticKeys) { + expect( + result.containsKey(keyName), + isTrue, + reason: '$keyName is included.', + ); + } + }); + + test('without full details', () { + final Map result = testTree + .toDiagnosticsNode() + .toJsonMap( + const DiagnosticsSerializationDelegate(), fullDetails: false + ); + expect(result.containsKey('properties'), isFalse); + expect(result.containsKey('children'), isFalse); + + for (final String keyName in essentialDiagnosticKeys) { + expect( + result.containsKey(keyName), + isTrue, + reason: '$keyName is included.', + ); + } + for (final String keyName in detailedDiagnosticKeys) { + expect( + result.containsKey(keyName), + isFalse, + reason: '$keyName is not included.', + ); + } }); test('subtreeDepth 1', () { @@ -279,7 +329,10 @@ class TestDiagnosticsSerializationDelegate implements DiagnosticsSerializationDe final NodeDelegator? nodeDelegator; @override - Map additionalNodeProperties(DiagnosticsNode node) { + Map additionalNodeProperties( + DiagnosticsNode node, { + bool fullDetails = true, + }) { return additionalNodePropertiesMap; } diff --git a/packages/flutter/test/gestures/monodrag_test.dart b/packages/flutter/test/gestures/monodrag_test.dart index 09616cca31cd0..516f1d8081e40 100644 --- a/packages/flutter/test/gestures/monodrag_test.dart +++ b/packages/flutter/test/gestures/monodrag_test.dart @@ -153,7 +153,7 @@ void main() { bool wasPanStartCalled = false; // Pump a tree with panable widget inside a CustomScrollView. The CustomScrollView - // has a more aggresive drag recognizer that will typically beat other drag + // has a more aggressive drag recognizer that will typically beat other drag // recognizers in the arena. This pan recognizer uses a smaller threshold to // accept the gesture, that should make it win the arena. await tester.pumpWidget( diff --git a/packages/flutter/test/material/about_test.dart b/packages/flutter/test/material/about_test.dart index afa52d2a25129..20c82598e37d7 100644 --- a/packages/flutter/test/material/about_test.dart +++ b/packages/flutter/test/material/about_test.dart @@ -1841,6 +1841,141 @@ void main() { expect(renderParagraph.text.style!.color, theme.primaryTextTheme.titleLarge!.color); }); }); + + testWidgets('Adaptive AboutDialog shows correct widget on each platform',(WidgetTester tester) async { + for (final TargetPlatform platform in [TargetPlatform.iOS, TargetPlatform.macOS]) { + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: platform), + home: const Material( + child: Center( + child: ElevatedButton( + onPressed: null, + child: Text('Go'), + ), + ), + ), + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showAdaptiveAboutDialog( + context: context, + applicationIcon: const Icon(Icons.abc), + applicationName: 'Test', + applicationVersion: '1.0.0', + applicationLegalese: 'Application Legalese', + children: [ + const Text('Test1'), + ], + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.byType(CupertinoDialogAction), findsWidgets); + } + + for (final TargetPlatform platform in [ + TargetPlatform.android, + TargetPlatform.fuchsia, + TargetPlatform.linux, + TargetPlatform.windows, + ]) { + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: platform), + home: const Material( + child: Center( + child: ElevatedButton( + onPressed: null, + child: Text('Go'), + ), + ), + ), + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showAboutDialog( + context: context, + applicationIcon: const Icon(Icons.abc), + applicationName: 'Test', + applicationVersion: '1.0.0', + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.byType(CupertinoDialogAction), findsNothing); + } + }); + + testWidgets('Adaptive AboutDialog closes correctly on each platform', (WidgetTester tester) async { + for (final TargetPlatform platform in [TargetPlatform.iOS, TargetPlatform.macOS]) { + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: platform), + home: const Material( + child: Center( + child: ElevatedButton( + onPressed: null, + child: Text('Go'), + ), + ), + ), + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showAdaptiveAboutDialog( + context: context, + applicationName: 'Test', + applicationVersion: '1.0.0', + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.byType(CupertinoDialogAction), findsWidgets); + + await tester.tap(find.text('Close')); + await tester.pumpAndSettle(); + expect(find.byType(CupertinoAlertDialog), findsNothing); + } + + for (final TargetPlatform platform in [ + TargetPlatform.android, + TargetPlatform.fuchsia, + TargetPlatform.linux, + TargetPlatform.windows, + ]) { + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(platform: platform), + home: const Material( + child: Center( + child: ElevatedButton( + onPressed: null, + child: Text('Go'), + ), + ), + ), + ), + ); + + final BuildContext context = tester.element(find.text('Go')); + + showAdaptiveAboutDialog( + context: context, + applicationName: 'Test', + applicationVersion: '1.0.0', + ); + + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.byType(TextButton), findsWidgets); + + await tester.tap(find.text('Close')); + await tester.pumpAndSettle(); + expect(find.byType(AlertDialog), findsNothing); + }}); } class FakeLicenseEntry extends LicenseEntry { diff --git a/packages/flutter/test/material/checkbox_theme_test.dart b/packages/flutter/test/material/checkbox_theme_test.dart index 5333fe44537fd..ac67ad16c956f 100644 --- a/packages/flutter/test/material/checkbox_theme_test.dart +++ b/packages/flutter/test/material/checkbox_theme_test.dart @@ -501,16 +501,16 @@ void main() { await tester.pumpWidget(buildCheckbox(seedColor: Colors.red)); await tester.pumpAndSettle(); - RenderBox getCheckboxRendeBox() { + RenderBox getCheckboxRenderBox() { return tester.renderObject(find.byType(Checkbox)); } - expect(getCheckboxRendeBox(), paints..drrect(color: colorScheme.primary)); + expect(getCheckboxRenderBox(), paints..drrect(color: colorScheme.primary)); await tester.pumpWidget(buildCheckbox(seedColor: Colors.blue)); await tester.pump(kPressTimeout); - expect(getCheckboxRendeBox(), paints..drrect(color: colorScheme.primary)); + expect(getCheckboxRenderBox(), paints..drrect(color: colorScheme.primary)); }); } diff --git a/packages/flutter/test/material/chip_test.dart b/packages/flutter/test/material/chip_test.dart index 7342eec9ed347..029b8c2221a03 100644 --- a/packages/flutter/test/material/chip_test.dart +++ b/packages/flutter/test/material/chip_test.dart @@ -5924,7 +5924,7 @@ void main() { ), ); - // Test the chechmark is not visible yet. + // Test the checkmark is not visible yet. expect(materialBox, isNot(paints..path(color:checkmarkColor))); expect(tester.getSize(find.byType(RawChip)).width, closeTo(132.6, 0.1)); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 4f2051dcb855b..7120416d264b8 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -4234,7 +4234,7 @@ void main() { ); // 12 is the left padding. - // TODO(bleroux): consider changing this padding because from M3 soec this should be 16. + // TODO(bleroux): consider changing this padding because from M3 spec this should be 16. expect(getLabelRect(tester).left, 12.0); // TODO(bleroux): consider changing the input text position because, based on M3 spec, // the expected horizontal position is 52 (12 padding, 24 icon, 16 gap between icon and input). @@ -7189,6 +7189,57 @@ void main() { expect(decoration.constraints, const BoxConstraints(minWidth: 10, maxWidth: 20, minHeight: 30, maxHeight: 40)); }); + testWidgets('InputDecoration with WidgetStateInputBorder', (WidgetTester tester) async { + const WidgetStateInputBorder outlineInputBorder = WidgetStateInputBorder.fromMap( + { + WidgetState.focused: OutlineInputBorder( + borderSide: BorderSide(color: Colors.blue, width: 4.0), + ), + WidgetState.hovered: OutlineInputBorder( + borderSide: BorderSide(color: Colors.cyan, width: 8.0), + ), + WidgetState.any: OutlineInputBorder(), + }, + ); + + RenderObject getBorder() { + return tester.renderObject( + find.descendant( + of: find.byType(TextField), + matching: find.byType(CustomPaint), + ), + ); + } + + final FocusNode focusNode = FocusNode(); + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: TextField( + focusNode: focusNode, + decoration: const InputDecoration( + border: outlineInputBorder, + ), + ), + ), + ), + ); + expect(getBorder(), paints..rrect(strokeWidth: 1.0)); + + focusNode.requestFocus(); + await tester.pumpAndSettle(); + expect(getBorder(), paints..rrect(color: Colors.blue, strokeWidth: 4.0)); + + focusNode.unfocus(); + final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); + await gesture.addPointer(location: tester.getCenter(find.byType(TextField))); + await tester.pumpAndSettle(); + expect(getBorder(), paints..rrect(color: Colors.cyan, strokeWidth: 8.0)); + + focusNode.dispose(); + }); + testWidgets('InputDecorator constrained to 0x0', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/17710 await tester.pumpWidget( diff --git a/packages/flutter/test/material/menu_anchor_test.dart b/packages/flutter/test/material/menu_anchor_test.dart index 4e5e204141179..e5ad19fa47a6b 100644 --- a/packages/flutter/test/material/menu_anchor_test.dart +++ b/packages/flutter/test/material/menu_anchor_test.dart @@ -4667,12 +4667,18 @@ void main() { return results; } + late final OverlayEntry overlayEntry; + addTearDown((){ + overlayEntry.remove(); + overlayEntry.dispose(); + }); + Widget boilerplate() { return MaterialApp( home: Overlay( key: overlayKey, initialEntries: [ - OverlayEntry( + overlayEntry = OverlayEntry( builder: (BuildContext context) { return Scaffold( body: Center( @@ -4718,6 +4724,19 @@ void main() { tester.renderObject(find.byWidget(rootOverlay)), ); }); + + // Regression test for https://github.com/flutter/flutter/issues/156572. + testWidgets('Unattached MenuController does not throw when calling close', (WidgetTester tester) async { + final MenuController controller = MenuController(); + controller.close(); + await tester.pump(); + expect(tester.takeException(), isNull); + }); + + testWidgets('Unattached MenuController returns false when calling isOpen', (WidgetTester tester) async { + final MenuController controller = MenuController(); + expect(controller.isOpen, false); + }); } List createTestMenus({ diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index 6d4ddde2f2717..3a1a76d546955 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -676,12 +676,8 @@ void main() { child: Text('XXX'), ), ); - bool popupMenu(Widget widget) { - final String widgetType = widget.runtimeType.toString(); - // TODO(mraleph): Remove the old case below. - return widgetType == '_PopupMenu' // normal case - || widgetType == '_PopupMenu'; // for old versions of Dart that don't reify method type arguments - } + + bool popupMenu(Widget widget) => widget.runtimeType.toString() == '_PopupMenu'; Future openMenu(TextDirection textDirection, Alignment alignment) async { return TestAsyncUtils.guard(() async { diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart index 1e47d929f2659..5fa8cde6e857e 100644 --- a/packages/flutter/test/material/tab_bar_theme_test.dart +++ b/packages/flutter/test/material/tab_bar_theme_test.dart @@ -1751,7 +1751,7 @@ void main() { await tester.pumpAndSettle(); // Ease in sine (accelerating). - double accelerateIntepolation(double fraction) { + double accelerateInterpolation(double fraction) { return 1.0 - math.cos((fraction * math.pi) / 2.0); } @@ -1762,8 +1762,8 @@ void main() { }) { const double indicatorWeight = 3.0; final double tabChangeProgress = (controller.index - controller.animation!.value).abs(); - final double leftFraction = accelerateIntepolation(tabChangeProgress); - final double rightFraction = accelerateIntepolation(tabChangeProgress); + final double leftFraction = accelerateInterpolation(tabChangeProgress); + final double rightFraction = accelerateInterpolation(tabChangeProgress); final RRect rrect = RRect.fromLTRBAndCorners( lerpDouble(rect.left, targetRect.left, leftFraction)!, diff --git a/packages/flutter/test/painting/border_test.dart b/packages/flutter/test/painting/border_test.dart index e4298f8c5499d..c2e38534c4b17 100644 --- a/packages/flutter/test/painting/border_test.dart +++ b/packages/flutter/test/painting/border_test.dart @@ -314,6 +314,22 @@ void main() { bottom: BorderSide(width: 20), ); expect(nonUniformBorderDirectional.dimensions, const EdgeInsetsDirectional.fromSTEB(5, 5, 0, 20)); + + const Border uniformWidthNonUniformStrokeAlignBorder = Border( + left: BorderSide(width: 10), + top: BorderSide(width: 10, strokeAlign: BorderSide.strokeAlignCenter), + right: BorderSide(width: 10, strokeAlign: BorderSide.strokeAlignOutside), + bottom: BorderSide(width: 10), + ); + expect(uniformWidthNonUniformStrokeAlignBorder.dimensions, const EdgeInsets.fromLTRB(10, 5, 0, 10)); + + const BorderDirectional uniformWidthNonUniformStrokeAlignBorderDirectional = BorderDirectional( + start: BorderSide(width: 10), + top: BorderSide(width: 10, strokeAlign: BorderSide.strokeAlignCenter), + end: BorderSide(width: 10, strokeAlign: BorderSide.strokeAlignOutside), + bottom: BorderSide(width: 10), + ); + expect(uniformWidthNonUniformStrokeAlignBorderDirectional.dimensions, const EdgeInsetsDirectional.fromSTEB(10, 5, 0, 10)); }); testWidgets('Non-Uniform Border variations', (WidgetTester tester) async { diff --git a/packages/flutter/test/rendering/sliver_fixed_extent_layout_test.dart b/packages/flutter/test/rendering/sliver_fixed_extent_layout_test.dart index b932fe5815bb3..4882f45d1eb99 100644 --- a/packages/flutter/test/rendering/sliver_fixed_extent_layout_test.dart +++ b/packages/flutter/test/rendering/sliver_fixed_extent_layout_test.dart @@ -51,8 +51,8 @@ void main() { const double extraValueToHaveRoundingIssues = 1e-11; test('should be 0 when item extent is 0', () { - const double offsetValueWhichDoesntCare = 1234; - final int actual = testGetMaxChildIndexForScrollOffset(offsetValueWhichDoesntCare, 0); + const double offsetValueWhichDoesNotCare = 1234; + final int actual = testGetMaxChildIndexForScrollOffset(offsetValueWhichDoesNotCare, 0); expect(actual, 0); }); diff --git a/packages/flutter/test/rendering/sliver_tree_test.dart b/packages/flutter/test/rendering/sliver_tree_test.dart index a38981b42e2c4..202502427a530 100644 --- a/packages/flutter/test/rendering/sliver_tree_test.dart +++ b/packages/flutter/test/rendering/sliver_tree_test.dart @@ -710,7 +710,7 @@ void main() { ..paragraph() // Icon ..paragraph(offset: const Offset(46.0, 48.0)) ..paragraph() // alpha icon animating - ..paragraph(offset: const Offset(56.0, -20.0)) // alpha naimating + ..paragraph(offset: const Offset(56.0, -20.0)) // alpha animating ..paragraph(offset: const Offset(56.0, 20.0)) // beta ..paragraph(offset: const Offset(56.0, 60.0)) // kappa ..paragraph() // Icon diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 90cf0868151f3..84e4d1923b902 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -4980,7 +4980,7 @@ void main() { await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500)); expect((findRenderEditable(tester).text! as TextSpan).text, 'β€’β€’β€’'); - }); + }, variant: const TargetPlatformVariant({ TargetPlatform.iOS, TargetPlatform.android, TargetPlatform.fuchsia, })); group('a11y copy/cut/paste', () { Future buildApp(MockTextSelectionControls controls, WidgetTester tester) { @@ -17017,6 +17017,45 @@ void main() { }, skip: !kIsWeb, // [intended] ); + + // Regression test for https://github.com/flutter/flutter/issues/156078. + testWidgets('when having focus regained after the app resumed', (WidgetTester tester) async { + Future setAppLifeCycleState(AppLifecycleState state) async { + final ByteData? message = const StringCodec().encodeMessage(state.toString()); + await TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .handlePlatformMessage('flutter/lifecycle', message, (_) {}); + } + + final TextEditingController controller = TextEditingController(text: 'Flutter!'); + addTearDown(controller.dispose); + final FocusNode focusNode = FocusNode(); + addTearDown(focusNode.dispose); + + await tester.pumpWidget( + MaterialApp( + home: Center( + child: EditableText( + key: ValueKey(controller.text), + controller: controller, + focusNode: focusNode, + autofocus: true, + style: Typography.material2018().black.titleMedium!, + cursorColor: Colors.blue, + backgroundCursorColor: Colors.grey, + ), + ), + ), + ); + + expect(focusNode.hasFocus, true); + expect(controller.selection, collapsedAtEnd('Flutter!').selection); + + await setAppLifeCycleState(AppLifecycleState.inactive); + await setAppLifeCycleState(AppLifecycleState.resumed); + + expect(focusNode.hasFocus, true); + expect(controller.selection, collapsedAtEnd('Flutter!').selection); + }, variant: TargetPlatformVariant.all()); }); testWidgets('EditableText respects MediaQuery.boldText', (WidgetTester tester) async { diff --git a/packages/flutter/test/widgets/html_element_view_test.dart b/packages/flutter/test/widgets/html_element_view_test.dart index d2778546cd57b..db296526a8de4 100644 --- a/packages/flutter/test/widgets/html_element_view_test.dart +++ b/packages/flutter/test/widgets/html_element_view_test.dart @@ -385,6 +385,75 @@ void main() { expect(createdElement, fakePlatformView.htmlElement); }); + + group('hitTestBehavior', () { + testWidgets('opaque by default', (WidgetTester tester) async { + final Key containerKey = UniqueKey(); + int taps = 0; + + await tester.pumpWidget( + GestureDetector( + onTap: () => taps++, + child: Container( + key: containerKey, + width: 200, + height: 200, + // Add a color to make it a visible container. This ensures that + // GestureDetector's default hit test behavior works. + color: const Color(0xFF00FF00), + child: HtmlElementView.fromTagName(tagName: 'div'), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(taps, isZero); + + await tester.tap( + find.byKey(containerKey), + warnIfMissed: false, + ); + + // Taps are still zero on the container because the HtmlElementView is + // opaque and prevents widgets behind it from receiving pointer events. + expect(taps, isZero); + }); + + testWidgets('can be set to transparent', (WidgetTester tester) async { + final Key containerKey = UniqueKey(); + int taps = 0; + + await tester.pumpWidget( + GestureDetector( + onTap: () => taps++, + child: Container( + key: containerKey, + width: 200, + height: 200, + // Add a color to make it a visible container. This ensures that + // GestureDetector's default hit test behavior works. + color: const Color(0xFF00FF00), + child: HtmlElementView.fromTagName( + tagName: 'div', + hitTestBehavior: PlatformViewHitTestBehavior.transparent, + ), + ), + ), + ); + await tester.pumpAndSettle(); + + expect(taps, isZero); + + await tester.tap( + find.byKey(containerKey), + warnIfMissed: false, + ); + + // The container can receive taps because the HtmlElementView is + // transparent from a hit testing perspective. + expect(taps, 1); + }); + }); }); } diff --git a/packages/flutter/test/widgets/image_rtl_test.dart b/packages/flutter/test/widgets/image_rtl_test.dart index f834959d50be6..0c2dfcc685810 100644 --- a/packages/flutter/test/widgets/image_rtl_test.dart +++ b/packages/flutter/test/widgets/image_rtl_test.dart @@ -7,40 +7,34 @@ import 'dart:ui' as ui show Image; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart'; -class TestImageProvider extends ImageProvider { - const TestImageProvider(this.image); +class _TestImageProvider extends ImageProvider<_TestImageProvider> { + _TestImageProvider(ui.Image image) { + _completer = OneFrameImageStreamCompleter( + SynchronousFuture(ImageInfo(image: image)), + ); + } - final ui.Image image; + late final OneFrameImageStreamCompleter _completer; @override - Future obtainKey(ImageConfiguration configuration) { - return SynchronousFuture(this); + Future<_TestImageProvider> obtainKey(ImageConfiguration configuration) { + return SynchronousFuture<_TestImageProvider>(this); } @override - ImageStreamCompleter loadImage(TestImageProvider key, ImageDecoderCallback decode) { - return OneFrameImageStreamCompleter( - SynchronousFuture(ImageInfo(image: image)), - ); + ImageStreamCompleter loadImage(_TestImageProvider key, ImageDecoderCallback decode) { + return _completer; } } void main() { - // TODO(polina-c): dispose ImageStreamCompleterHandle, https://github.com/flutter/flutter/issues/145599 [leaks-to-clean] - LeakTesting.settings = LeakTesting.settings.withIgnoredAll(); - late ui.Image testImage; - setUpAll(() async { + setUp(() async { testImage = await createTestImage(width: 16, height: 9); }); - tearDownAll(() { - testImage.dispose(); - }); - testWidgets('DecorationImage RTL with alignment topEnd and match', (WidgetTester tester) async { await tester.pumpWidget( Directionality( @@ -51,7 +45,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, matchTextDirection: true, @@ -90,7 +84,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, matchTextDirection: true, @@ -126,7 +120,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, ), @@ -161,7 +155,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, ), @@ -196,7 +190,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -228,7 +222,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, ), ), @@ -255,7 +249,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -283,7 +277,7 @@ void main() { height: 50.0, decoration: BoxDecoration( image: DecorationImage( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -310,7 +304,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, matchTextDirection: true, @@ -336,6 +330,8 @@ void main() { ..restore(), ); expect(find.byType(SizedBox), isNot(paints..scale()..scale())); + + imageCache.clear(); }); testWidgets('Image LTR with alignment topEnd (and pointless match)', (WidgetTester tester) async { @@ -347,7 +343,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, matchTextDirection: true, @@ -381,7 +377,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, ), @@ -414,7 +410,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: AlignmentDirectional.topEnd, repeat: ImageRepeat.repeatX, ), @@ -447,7 +443,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -475,7 +471,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, ), ), @@ -500,7 +496,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -526,7 +522,7 @@ void main() { width: 100.0, height: 50.0, child: Image( - image: TestImageProvider(testImage), + image: _TestImageProvider(testImage), alignment: Alignment.centerRight, matchTextDirection: true, ), @@ -544,22 +540,25 @@ void main() { }); testWidgets('Image - Switch needing direction', (WidgetTester tester) async { + final _TestImageProvider provider = _TestImageProvider(testImage); + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Image( - image: TestImageProvider(testImage), + image: provider, alignment: Alignment.centerRight, ), ), duration: Duration.zero, phase: EnginePhase.layout, // so that we don't try to paint the fake images ); + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Image( - image: TestImageProvider(testImage), + image: provider, alignment: AlignmentDirectional.centerEnd, matchTextDirection: true, ), @@ -567,16 +566,19 @@ void main() { duration: Duration.zero, phase: EnginePhase.layout, // so that we don't try to paint the fake images ); + await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: Image( - image: TestImageProvider(testImage), + image: provider, alignment: Alignment.centerRight, ), ), duration: Duration.zero, phase: EnginePhase.layout, // so that we don't try to paint the fake images ); + + imageCache.clear(); }); } diff --git a/packages/flutter/test/widgets/image_test.dart b/packages/flutter/test/widgets/image_test.dart index 033d60351bdc3..064c6730aec35 100644 --- a/packages/flutter/test/widgets/image_test.dart +++ b/packages/flutter/test/widgets/image_test.dart @@ -824,8 +824,7 @@ void main() { }); testWidgets('Precache removes original listener immediately after future completes, does not crash on successive calls #25143', - // TODO(polina-c): dispose ImageStreamCompleterHandle, https://github.com/flutter/flutter/issues/145599 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final _TestImageStreamCompleter imageStreamCompleter = _TestImageStreamCompleter(); final _TestImageProvider provider = _TestImageProvider(streamCompleter: imageStreamCompleter); @@ -845,12 +844,17 @@ void main() { // Make sure the first listener can be called re-entrantly final ImageInfo imageInfo = ImageInfo(image: image10x10); + listeners[1].onImage(imageInfo.clone(), false); listeners[1].onImage(imageInfo.clone(), false); // Make sure the second listener can be called re-entrantly. listeners[0].onImage(imageInfo.clone(), false); listeners[0].onImage(imageInfo.clone(), false); + + imageInfo.dispose(); + imageStreamCompleter.dispose(); + imageCache.clear(); }); testWidgets('Precache completes with onError on error', (WidgetTester tester) async { @@ -1022,8 +1026,7 @@ void main() { }); testWidgets('Image invokes frameBuilder with correct frameNumber argument', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final ui.Codec codec = (await tester.runAsync(() { return ui.instantiateImageCodec(Uint8List.fromList(kAnimatedGif)); @@ -1094,8 +1097,7 @@ void main() { }); testWidgets('Image invokes frameBuilder with correct wasSynchronouslyLoaded=true', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final _TestImageStreamCompleter streamCompleter = _TestImageStreamCompleter(ImageInfo(image: image10x10.clone())); final _TestImageProvider imageProvider = _TestImageProvider(streamCompleter: streamCompleter); @@ -1155,8 +1157,7 @@ void main() { }); testWidgets('Image state handles enabling and disabling of tickers', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final ui.Codec codec = (await tester.runAsync(() { return ui.instantiateImageCodec(Uint8List.fromList(kAnimatedGif)); @@ -1621,8 +1622,7 @@ void main() { }); testWidgets('precacheImage allows time to take over weak reference', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final _TestImageProvider provider = _TestImageProvider(); late Future precache; @@ -1674,10 +1674,7 @@ void main() { expect(provider.loadCallCount, 1); }); - testWidgets('evict an image during precache', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), - (WidgetTester tester) async { + testWidgets('evict an image during precache', (WidgetTester tester) async { // This test checks that the live image tracking does not hold on to a // pending image that will never complete because it has been evicted from // the cache. @@ -1707,6 +1704,8 @@ void main() { await tester.pump(); expect(imageCache.statusForKey(provider).keepAlive, true); expect(imageCache.statusForKey(provider).live, false); + + imageCache.clear(); }); }); @@ -1791,10 +1790,7 @@ void main() { } testWidgets( - 'Rotated images', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), - (WidgetTester tester) async { + 'Rotated images', (WidgetTester tester) async { await testRotatedImage(tester, true); await testRotatedImage(tester, false); }, @@ -1802,10 +1798,7 @@ void main() { ); testWidgets( - 'Image opacity', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), - (WidgetTester tester) async { + 'Image opacity', (WidgetTester tester) async { final Key key = UniqueKey(); await tester.pumpWidget(RepaintBoundary( key: key, @@ -1854,8 +1847,7 @@ void main() { ); testWidgets('Reports image size when painted', - // TODO(polina-c): make sure images are disposed, https://github.com/flutter/flutter/issues/141388 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { late ImageSizeInfo imageSizeInfo; int count = 0; @@ -1966,8 +1958,7 @@ void main() { }); testWidgets('Load a good image after a bad image was loaded should not call errorBuilder', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final UniqueKey errorKey = UniqueKey(); final ui.Image image = (await tester.runAsync(() => createTestImage()))!; @@ -2067,8 +2058,7 @@ void main() { }); testWidgets('Animated GIFs do not require layout for subsequent frames', - // TODO(polina-c): clean up leaks, https://github.com/flutter/flutter/issues/134787 [leaks-to-clean] - experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), + experimentalLeakTesting: LeakTesting.settings.withIgnoredAll(), // The test leaks by design, see [_TestImageStreamCompleter]. (WidgetTester tester) async { final ui.Codec codec = (await tester.runAsync(() { return ui.instantiateImageCodec(Uint8List.fromList(kAnimatedGif)); @@ -2196,6 +2186,14 @@ class _TestImageProvider extends ImageProvider { String toString() => '${describeIdentity(this)}()'; } +/// An [ImageStreamCompleter] that gives access to the added listeners. +/// +/// Such an access to listeners is hacky, +/// because it breaks encapsulation by allowing to invoke listeners without +/// taking care about lifecycle of the created images, that may result in not disposed images. +/// +/// That's why some tests that use it +/// are opted out from leak tracking. class _TestImageStreamCompleter extends ImageStreamCompleter { _TestImageStreamCompleter([this._currentImage]); @@ -2243,6 +2241,11 @@ class _TestImageStreamCompleter extends ImageStreamCompleter { listener.onError?.call(exception, stackTrace); } } + + void dispose() { + final List listenersCopy = listeners.toList(); + listenersCopy.forEach(removeListener); + } } class _DebouncingImageProvider extends ImageProvider { diff --git a/packages/flutter/test/widgets/router_test.dart b/packages/flutter/test/widgets/router_test.dart index f6fa0e22b1e30..ff6029ec30a68 100644 --- a/packages/flutter/test/widgets/router_test.dart +++ b/packages/flutter/test/widgets/router_test.dart @@ -41,9 +41,7 @@ void main() { expect(find.text('update'), findsOneWidget); }); - testWidgets('Router respects update order', - experimentalLeakTesting: LeakTesting.settings.withCreationStackTrace(), - (WidgetTester tester) async { + testWidgets('Router respects update order', (WidgetTester tester) async { final SimpleRouteInformationProvider provider = SimpleRouteInformationProvider(); addTearDown(provider.dispose); provider.value = RouteInformation( diff --git a/packages/flutter/test/widgets/selectable_region_test.dart b/packages/flutter/test/widgets/selectable_region_test.dart index a38bd014b7e1c..38db49b13396f 100644 --- a/packages/flutter/test/widgets/selectable_region_test.dart +++ b/packages/flutter/test/widgets/selectable_region_test.dart @@ -1109,7 +1109,7 @@ void main() { // on native Android is 2 when the pointer device kind is not precise like // for a touch. // - // On iOS the selection is maintained because the tap occured on the active + // On iOS the selection is maintained because the tap occurred on the active // selection. expect(paragraph.selections[0], defaultTargetPlatform == TargetPlatform.iOS ? const TextSelection(baseOffset: 0, extentOffset: 5) : const TextSelection.collapsed(offset: 2)); }, diff --git a/packages/flutter/test/widgets/semantics_test.dart b/packages/flutter/test/widgets/semantics_test.dart index 8b0d18375f25e..66dcd7ddb0f0b 100644 --- a/packages/flutter/test/widgets/semantics_test.dart +++ b/packages/flutter/test/widgets/semantics_test.dart @@ -1819,7 +1819,7 @@ void main() { } }); - testWidgets('parent heading level takes precendence when it absorbs a child', (WidgetTester tester) async { + testWidgets('parent heading level takes precedence when it absorbs a child', (WidgetTester tester) async { final SemanticsTester semantics = SemanticsTester(tester); Future pumpHeading(int? level) async { diff --git a/packages/flutter/test/widgets/sliver_main_axis_group_test.dart b/packages/flutter/test/widgets/sliver_main_axis_group_test.dart index e638dca79b2ac..7c9239972beeb 100644 --- a/packages/flutter/test/widgets/sliver_main_axis_group_test.dart +++ b/packages/flutter/test/widgets/sliver_main_axis_group_test.dart @@ -797,6 +797,64 @@ void main() { await tester.pumpAndSettle(); expect(tester.getTopLeft(find.byKey(key)), Offset.zero); }); + + testWidgets('SliverMainAxisGroup scrolls to the correct position when focusing on a text field within a header', (WidgetTester tester) async { + final ScrollController controller = ScrollController(); + addTearDown(controller.dispose); + final FocusNode textFieldFocus = FocusNode(); + addTearDown(textFieldFocus.dispose); + final FocusNode textFieldFocus2 = FocusNode(); + addTearDown(textFieldFocus2.dispose); + const ValueKey firstTextFieldKey = ValueKey(1); + + await tester.pumpWidget( + _buildSliverMainAxisGroup( + controller: controller, + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: _SliverTitleDelegate( + child: Container( + color: Colors.red, + height: 60.0, + ), + height: 60.0, + ), + ), + SliverToBoxAdapter( + child: Material( + child: TextField( + key: firstTextFieldKey, + focusNode: textFieldFocus, + ), + )), + SliverToBoxAdapter( + child: Container( + color: Colors.green, + height: 500, + ), + ), + SliverToBoxAdapter( + child: Material( + child: TextField( + focusNode: textFieldFocus2, + ), + )), + ], + ), + ); + + await tester.pumpAndSettle(); + await tester.pumpAndSettle(); + + textFieldFocus2.requestFocus(); + await tester.pumpAndSettle(); + + textFieldFocus.requestFocus(); + await tester.pumpAndSettle(); + + expect(tester.getTopLeft(find.byKey(firstTextFieldKey)), const Offset(0, 60)); + }); } Widget _buildSliverList({ @@ -875,3 +933,26 @@ class TestDelegate extends SliverPersistentHeaderDelegate { @override bool shouldRebuild(TestDelegate oldDelegate) => true; } + +class _SliverTitleDelegate extends SliverPersistentHeaderDelegate { + _SliverTitleDelegate({ + required this.height, + required this.child, + }); + final double height; + final Widget child; + + @override + double get minExtent => height; + @override + double get maxExtent => height; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + bool shouldRebuild(_SliverTitleDelegate oldDelegate) => true; +} diff --git a/packages/flutter/test/widgets/sliver_resizing_header_test.dart b/packages/flutter/test/widgets/sliver_resizing_header_test.dart index 1fd7a08422507..40ed6ae573e5c 100644 --- a/packages/flutter/test/widgets/sliver_resizing_header_test.dart +++ b/packages/flutter/test/widgets/sliver_resizing_header_test.dart @@ -199,7 +199,7 @@ void main() { expect(find.text('header'), findsNothing); }); - testWidgets('SliverResizingHeader with identcial min/max prototypes is effectively a pinned header', (WidgetTester tester) async { + testWidgets('SliverResizingHeader with identical min/max prototypes is effectively a pinned header', (WidgetTester tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( diff --git a/packages/flutter/test/widgets/tap_region_test.dart b/packages/flutter/test/widgets/tap_region_test.dart index 8b6eda98af81e..9e996f90dd662 100644 --- a/packages/flutter/test/widgets/tap_region_test.dart +++ b/packages/flutter/test/widgets/tap_region_test.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { - testWidgets('TapRegionSurface detects outside taps', (WidgetTester tester) async { + testWidgets('TapRegionSurface detects outside tap down events', (WidgetTester tester) async { final Set tappedOutside = {}; await tester.pumpWidget( Directionality( @@ -56,7 +56,8 @@ void main() { tester.getCenter(finder), kind: PointerDeviceKind.mouse, ); - await gesture.up(); + // We intentionally don't call up() here because we're testing the down event. + await gesture.cancel(); await gesture.removePointer(); } @@ -101,6 +102,91 @@ void main() { expect(tappedOutside, isEmpty); }); + testWidgets('TapRegionSurface detects outside tap up events', (WidgetTester tester) async { + final Set tappedOutside = {}; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: TapRegionSurface( + child: Row( + children: [ + const Text('Outside'), + TapRegion( + onTapUpOutside: (PointerEvent event) { + tappedOutside.add('No Group'); + }, + child: const Text('No Group'), + ), + TapRegion( + groupId: 1, + onTapUpOutside: (PointerEvent event) { + tappedOutside.add('Group 1 A'); + }, + child: const Text('Group 1 A'), + ), + TapRegion( + groupId: 1, + onTapUpOutside: (PointerEvent event) { + tappedOutside.add('Group 1 B'); + }, + child: const Text('Group 1 B'), + ), + ], + ), + ), + ), + ); + + await tester.pump(); + + Future click(Finder finder) async { + final TestGesture gesture = await tester.startGesture( + tester.getCenter(finder), + kind: PointerDeviceKind.mouse, + ); + expect(tappedOutside, isEmpty); // No callbacks should been called before up event. + await gesture.up(); + await gesture.removePointer(); + } + + expect(tappedOutside, isEmpty); + + await click(find.text('No Group')); + expect( + tappedOutside, + unorderedEquals({ + 'Group 1 A', + 'Group 1 B', + })); + tappedOutside.clear(); + + await click(find.text('Group 1 A')); + expect( + tappedOutside, + equals({ + 'No Group', + })); + tappedOutside.clear(); + + await click(find.text('Group 1 B')); + expect( + tappedOutside, + equals({ + 'No Group', + })); + tappedOutside.clear(); + + await click(find.text('Outside')); + expect( + tappedOutside, + unorderedEquals({ + 'No Group', + 'Group 1 A', + 'Group 1 B', + })); + tappedOutside.clear(); + }); + testWidgets('TapRegionSurface consumes outside taps when asked', (WidgetTester tester) async { final Set tappedOutside = {}; int propagatedTaps = 0; @@ -206,7 +292,7 @@ void main() { expect(tappedOutside, isEmpty); }); - testWidgets('TapRegionSurface detects inside taps', (WidgetTester tester) async { + testWidgets('TapRegionSurface detects inside tap down events', (WidgetTester tester) async { final Set tappedInside = {}; await tester.pumpWidget( Directionality( @@ -253,7 +339,8 @@ void main() { tester.getCenter(finder), kind: PointerDeviceKind.mouse, ); - await gesture.up(); + // We intentionally don't call up() here because we're testing the down event. + await gesture.cancel(); await gesture.removePointer(); } @@ -293,6 +380,86 @@ void main() { expect(tappedInside, isEmpty); }); + testWidgets('TapRegionSurface detects inside tap up events', (WidgetTester tester) async { + final Set tappedInside = {}; + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: TapRegionSurface( + child: Row( + children: [ + const Text('Outside'), + TapRegion( + onTapUpInside: (PointerEvent event) { + tappedInside.add('No Group'); + }, + child: const Text('No Group'), + ), + TapRegion( + groupId: 1, + onTapUpInside: (PointerEvent event) { + tappedInside.add('Group 1 A'); + }, + child: const Text('Group 1 A'), + ), + TapRegion( + groupId: 1, + onTapUpInside: (PointerEvent event) { + tappedInside.add('Group 1 B'); + }, + child: const Text('Group 1 B'), + ), + ], + ), + ), + ), + ); + + await tester.pump(); + + Future click(Finder finder) async { + final TestGesture gesture = await tester.startGesture( + tester.getCenter(finder), + kind: PointerDeviceKind.mouse, + ); + expect(tappedInside, isEmpty); // No callbacks should been called before up event. + await gesture.up(); + await gesture.removePointer(); + } + + expect(tappedInside, isEmpty); + + await click(find.text('No Group')); + expect( + tappedInside, + unorderedEquals({ + 'No Group', + })); + tappedInside.clear(); + + await click(find.text('Group 1 A')); + expect( + tappedInside, + equals({ + 'Group 1 A', + 'Group 1 B', + })); + tappedInside.clear(); + + await click(find.text('Group 1 B')); + expect( + tappedInside, + equals({ + 'Group 1 A', + 'Group 1 B', + })); + tappedInside.clear(); + + await click(find.text('Outside')); + expect(tappedInside, isEmpty); + tappedInside.clear(); + }); + testWidgets('TapRegionSurface detects inside taps correctly with behavior', (WidgetTester tester) async { final Set tappedInside = {}; const ValueKey noGroupKey = ValueKey('No Group'); diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index e7c673158c288..e4e9c2fb5c760 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -1982,6 +1982,14 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { return childJson['createdByLocalProject'] == true; } + /// Returns whether the child is missing the "type" field. + /// + /// This should always be true for nodes in the widget tree without + /// full details. + bool isMissingType(Map childJson) { + return childJson['type'] == null; + } + /// Returns whether the child has a description matching [description]. bool hasDescription( Map childJson, { @@ -2278,6 +2286,79 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ))! as Map; + expect( + allChildrenSatisfyCondition(rootJson, + condition: isMissingType, + ), + isFalse, + ); + expect( + allChildrenSatisfyCondition(rootJson, + condition: wasCreatedByLocalProject, + ), + isFalse, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'a'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + !hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, + ); + }); + + testWidgets( + 'tree without full details using ext.flutter.inspector.getRootWidgetTree', + (WidgetTester tester) async { + const String group = 'test-group'; + + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = + await selectedWidgetResponseForElement(elementA); + + final Map creationLocation = + verifyAndReturnCreationLocation(jsonA); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); + + final Map rootJson = (await service.testExtension( + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'false', + 'fullDetails': 'false', + }, + ))! as Map; + + expect( + allChildrenSatisfyCondition(rootJson, + condition: isMissingType, + ), + isTrue, + ); + expect( allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject, @@ -2337,6 +2418,79 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { }, ))! as Map; + expect( + allChildrenSatisfyCondition(rootJson, + condition: isMissingType, + ), + isFalse, + ); + expect( + allChildrenSatisfyCondition(rootJson, + condition: wasCreatedByLocalProject, + ), + isFalse, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'a'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'b'); + }, + ), + isTrue, + ); + expect( + oneChildSatisfiesCondition(rootJson, condition: (Map child) { + return hasDescription(child, description: 'Text') && + wasCreatedByLocalProject(child) && + hasTextPreview(child, preview: 'c'); + }, + ), + isTrue, + ); + }); + + testWidgets( + 'tree without full details and with previews using ext.flutter.inspector.getRootWidgetTree', + (WidgetTester tester) async { + const String group = 'test-group'; + + await pumpWidgetTreeWithABC(tester); + final Element elementA = findElementABC('a'); + final Map jsonA = + await selectedWidgetResponseForElement(elementA); + + final Map creationLocation = + verifyAndReturnCreationLocation(jsonA); + final String testFile = verifyAndReturnTestFile(creationLocation); + addPubRootDirectoryFor(testFile); + + final Map rootJson = (await service.testExtension( + WidgetInspectorServiceExtensions.getRootWidgetTree.name, + { + 'groupName': group, + 'isSummaryTree': 'false', + 'withPreviews': 'true', + 'fullDetails': 'false', + }, + ))! as Map; + + + expect( + allChildrenSatisfyCondition(rootJson, + condition: isMissingType, + ), + isTrue, + ); expect( allChildrenSatisfyCondition(rootJson, condition: wasCreatedByLocalProject, @@ -5390,6 +5544,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { node.toJsonMap(const DiagnosticsSerializationDelegate()), equals({ 'description': 'description of the deep link', + 'shouldIndent': true, 'type': 'DevToolsDeepLinkProperty', 'name': '', 'style': 'singleLine', diff --git a/packages/flutter/test_fixes/material/material.dart b/packages/flutter/test_fixes/material/material.dart index 41938f6c16a09..eca45b015e385 100644 --- a/packages/flutter/test_fixes/material/material.dart +++ b/packages/flutter/test_fixes/material/material.dart @@ -306,4 +306,12 @@ void main() { final PlatformMenuBar platformMenuBar = PlatformMenuBar(menus: [], body: const SizedBox()); final Widget bodyValue = platformMenuBar.body; + + // Changes made in https://github.com/flutter/flutter/pull/154972 + final InputBorder outlineBorder = MaterialStateOutlineInputBorder.resolveWith( + (states) => const OutlineInputBorder(), + ); + final InputBorder underlineBorder = MaterialStateUnderlineInputBorder.resolveWith( + (states) => const UnderlineInputBorder(), + ); } diff --git a/packages/flutter/test_fixes/material/material.dart.expect b/packages/flutter/test_fixes/material/material.dart.expect index 512a8198bb207..649ea9d9e6adf 100644 --- a/packages/flutter/test_fixes/material/material.dart.expect +++ b/packages/flutter/test_fixes/material/material.dart.expect @@ -302,4 +302,12 @@ void main() { final PlatformMenuBar platformMenuBar = PlatformMenuBar(menus: [], child: const SizedBox()); final Widget bodyValue = platformMenuBar.child; + + // Changes made in https://github.com/flutter/flutter/pull/154972 + final InputBorder outlineBorder = WidgetStateInputBorder.resolveWith( + (states) => const OutlineInputBorder(), + ); + final InputBorder underlineBorder = WidgetStateInputBorder.resolveWith( + (states) => const UnderlineInputBorder(), + ); } diff --git a/packages/flutter/test_fixes/material/theme_data.dart b/packages/flutter/test_fixes/material/theme_data.dart index c9c5d929cb5bc..c8e8ae7a25c83 100644 --- a/packages/flutter/test_fixes/material/theme_data.dart +++ b/packages/flutter/test_fixes/material/theme_data.dart @@ -237,4 +237,12 @@ void main() { // Changes made in https://github.com/flutter/flutter/pull/131455 ThemeData themeData = ThemeData.copyWith(useMaterial3: false); + + // Changes made in https://github.com/flutter/flutter/pull/155072 + ThemeData themeData = ThemeData(); + themeData = ThemeData(dialogBackgroundColor: Colors.orange); + themeData = ThemeData(dialogBackgroundColor: Colors.orange, dialogTheme: DialogThemeData(backgroundColor: Colors.red)); + themeData = themeData.copyWith(dialogBackgroundColor: Colors.orange); + themeData = themeData.copyWith(dialogBackgroundColor: Colors.orange, dialogTheme: DialogThemeData(backgroundColor: Colors.red)); + themeData.dialogBackgroundColor; // Removing field reference not supported. } diff --git a/packages/flutter/test_fixes/material/theme_data.dart.expect b/packages/flutter/test_fixes/material/theme_data.dart.expect index 2006033870307..2138766eae8f0 100644 --- a/packages/flutter/test_fixes/material/theme_data.dart.expect +++ b/packages/flutter/test_fixes/material/theme_data.dart.expect @@ -443,4 +443,12 @@ void main() { // Changes made in https://github.com/flutter/flutter/pull/131455 ThemeData themeData = ThemeData.copyWith(); + + // Changes made in https://github.com/flutter/flutter/pull/155072 + ThemeData themeData = ThemeData(); + themeData = ThemeData(dialogTheme: DialogThemeData(backgroundColor: Colors.orange)); + themeData = ThemeData(dialogTheme: DialogThemeData(backgroundColor: Colors.red)); + themeData = themeData.copyWith(dialogTheme: DialogThemeData(backgroundColor: Colors.orange)); + themeData = themeData.copyWith(dialogTheme: DialogThemeData(backgroundColor: Colors.red)); + themeData.dialogBackgroundColor; // Removing field reference not supported. } diff --git a/packages/flutter/test_fixes/widgets/widgets.dart b/packages/flutter/test_fixes/widgets/widgets.dart index 9d28b4e827dd9..d97033269aaaa 100644 --- a/packages/flutter/test_fixes/widgets/widgets.dart +++ b/packages/flutter/test_fixes/widgets/widgets.dart @@ -174,7 +174,7 @@ void main() { final PlatformMenuBar platformMenuBar = PlatformMenuBar(menus: [], body: const SizedBox()); final Widget bodyValue = platformMenuBar.body; - // Changes made in TBD + // Changes made in https://github.com/flutter/flutter/pull/139260 final NavigatorState state = Navigator.of(context); state.focusScopeNode; } diff --git a/packages/flutter/test_fixes/widgets/widgets.dart.expect b/packages/flutter/test_fixes/widgets/widgets.dart.expect index a2569cbe61644..684b78627c89b 100644 --- a/packages/flutter/test_fixes/widgets/widgets.dart.expect +++ b/packages/flutter/test_fixes/widgets/widgets.dart.expect @@ -174,7 +174,7 @@ void main() { final PlatformMenuBar platformMenuBar = PlatformMenuBar(menus: [], child: const SizedBox()); final Widget bodyValue = platformMenuBar.child; - // Changes made in TBD + // Changes made in https://github.com/flutter/flutter/pull/139260 final NavigatorState state = Navigator.of(context); state.focusNode.enclosingScope!; } diff --git a/packages/flutter/test_private/test/pubspec.yaml b/packages/flutter/test_private/test/pubspec.yaml index e4db8aca72c07..c74a3a84b6360 100644 --- a/packages/flutter/test_private/test/pubspec.yaml +++ b/packages/flutter/test_private/test/pubspec.yaml @@ -22,8 +22,8 @@ dependencies: async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,4 +47,4 @@ dev_dependencies: process: 5.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: ddc8 +# PUBSPEC CHECKSUM: c2ca diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml index 60df963bd2ff9..7169c093e28ab 100644 --- a/packages/flutter_driver/pubspec.yaml +++ b/packages/flutter_driver/pubspec.yaml @@ -23,8 +23,8 @@ dependencies: characters: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +76,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: d614 +# PUBSPEC CHECKSUM: f116 diff --git a/packages/flutter_goldens/pubspec.yaml b/packages/flutter_goldens/pubspec.yaml index 20770b2b434fc..0fe2da1b42926 100644 --- a/packages/flutter_goldens/pubspec.yaml +++ b/packages/flutter_goldens/pubspec.yaml @@ -24,8 +24,8 @@ dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +40,4 @@ dependencies: vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: ddc8 +# PUBSPEC CHECKSUM: c2ca diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml index ba54556b60cc3..af2401419c6a4 100644 --- a/packages/flutter_localizations/pubspec.yaml +++ b/packages/flutter_localizations/pubspec.yaml @@ -28,8 +28,8 @@ dev_dependencies: async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,4 +40,4 @@ dev_dependencies: test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 17a5 +# PUBSPEC CHECKSUM: 43a7 diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml index 9849d573b414b..aa7714376dd31 100644 --- a/packages/flutter_test/pubspec.yaml +++ b/packages/flutter_test/pubspec.yaml @@ -34,13 +34,13 @@ dependencies: vector_math: 2.1.4 # Used to detect memory leaks with `testWidgets`. - leak_tracker_flutter_testing: 3.0.8 + leak_tracker_flutter_testing: 3.0.9 async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" characters: 1.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.15.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -58,4 +58,4 @@ dev_dependencies: sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 53a3 +# PUBSPEC CHECKSUM: 00a5 diff --git a/packages/flutter_test/test/controller_test.dart b/packages/flutter_test/test/controller_test.dart index 8d8b93bb8f50b..4af6ea81eb8bc 100644 --- a/packages/flutter_test/test/controller_test.dart +++ b/packages/flutter_test/test/controller_test.dart @@ -1093,7 +1093,7 @@ void main() { ..removeAt(0) ..removeLast() ..mapIndexed((int i, Matcher element) => i.isEven ? element : null) - .whereNotNull(); + .nonNulls; expect( tester.semantics.simulatedAccessibilityTraversal(), diff --git a/packages/flutter_tools/gradle/settings.gradle.legacy_versions b/packages/flutter_tools/gradle/settings.gradle.legacy_versions deleted file mode 100644 index fc40e6d933cd2..0000000000000 --- a/packages/flutter_tools/gradle/settings.gradle.legacy_versions +++ /dev/null @@ -1,59 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} -;EOF -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withInputStream { stream -> plugins.load(stream) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} -;EOF -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" -;EOF -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy index 8c2ea18569416..fbbca50bf0e0e 100644 --- a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy +++ b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy @@ -14,6 +14,7 @@ import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.Plugin import org.gradle.api.Task +import org.gradle.api.UnknownTaskException import org.gradle.api.file.CopySpec import org.gradle.api.file.FileCollection import org.gradle.api.logging.LogLevel @@ -687,6 +688,10 @@ class FlutterPlugin implements Plugin { } catch (FileNotFoundException ignored) { throw new GradleException("settings.gradle/settings.gradle.kts does not exist: ${settingsGradleFile(project).absolutePath}") } + // TODO(matanlurey): https://github.com/flutter/flutter/issues/48918. + project.logger.quiet("Warning: This project is still reading the deprecated '.flutter-plugins. file.") + project.logger.quiet("In an upcoming stable release support for this file will be completely removed and your build will fail.") + project.logger.quiet("See https:/flutter.dev/to/flutter-plugins-configuration.") List> deps = getPluginDependencies(project) List plugins = getPluginList(project).collect { it.name as String } deps.removeIf { plugins.contains(it.name) } @@ -1359,24 +1364,21 @@ class FlutterPlugin implements Plugin { // The following tasks use the output of copyFlutterAssetsTask, // so it's necessary to declare it as an dependency since Gradle 8. // See https://docs.gradle.org/8.1/userguide/validation_problems.html#implicit_dependency. - def compressAssetsTask = project.tasks.findByName("compress${variant.name.capitalize()}Assets") - if (compressAssetsTask) { - compressAssetsTask.dependsOn(copyFlutterAssetsTask) - } - - def bundleAarTask = project.tasks.findByName("bundle${variant.name.capitalize()}Aar") - if (bundleAarTask) { - bundleAarTask.dependsOn(copyFlutterAssetsTask) - } - - def bundleAarTaskWithLint = project.tasks.findByName("bundle${variant.name.capitalize()}LocalLintAar") - if (bundleAarTaskWithLint) { - bundleAarTaskWithLint.dependsOn(copyFlutterAssetsTask) + def tasksToCheck = [ + "compress${variant.name.capitalize()}Assets", + "bundle${variant.name.capitalize()}Aar", + "bundle${variant.name.capitalize()}LocalLintAar" + ] + tasksToCheck.each { taskTocheck -> + try { + project.tasks.named(taskTocheck).configure { task -> + task.dependsOn(copyFlutterAssetsTask) + } + } catch (UnknownTaskException ignored) { + } } - return copyFlutterAssetsTask } // end def addFlutterDeps - if (isFlutterAppProject()) { project.android.applicationVariants.all { variant -> Task assembleTask = getAssembleTask(variant) @@ -1795,7 +1797,7 @@ class FlutterTask extends BaseFlutterTask { // : String depText = dependenciesFile.text // So we split list of files by non-escaped(by backslash) space, - def matcher = depText.split(": ")[inputs ? 1 : 0] =~ /(\\ |[^\s])+/ + def matcher = depText.split(": ")[inputs ? 1 : 0] =~ /(\\ |\S)+/ // then we replace all escaped spaces with regular spaces def depList = matcher.collect{ it[0].replaceAll("\\\\ ", " ") } return project.files(depList) diff --git a/packages/flutter_tools/lib/src/android/gradle_errors.dart b/packages/flutter_tools/lib/src/android/gradle_errors.dart index 09540a05ba4a6..fa45117775417 100644 --- a/packages/flutter_tools/lib/src/android/gradle_errors.dart +++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart @@ -687,9 +687,6 @@ ${_getAgpLocation(project)}''', eventLabel: 'r8-dexing-bug-in-AGP-7.3' ); -@visibleForTesting -const String jlinkErrorMessage = '> Error while executing process'; - @visibleForTesting final GradleHandledError jlinkErrorWithJava21AndSourceCompatibility = GradleHandledError( test: (String line) => line.contains('> Error while executing process')&& line.contains('jlink'), diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index ec421d4b27f0d..2b1705058ed3e 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -431,8 +431,9 @@ abstract class Artifacts { // and [mode] combination. String getEngineType(TargetPlatform platform, [ BuildMode? mode ]); - /// Whether these artifacts correspond to a non-versioned local engine. - bool get isLocalEngine; + /// Whether these artifacts use any locally built files that are not part of + /// a versioned engine. + bool get usesLocalArtifacts; /// If these artifacts are bound to a local engine build, returns info about /// the location and name of the local engine, otherwise returns null. @@ -906,7 +907,7 @@ class CachedArtifacts implements Artifacts { } @override - bool get isLocalEngine => false; + bool get usesLocalArtifacts => false; } TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils operatingSystemUtils) { @@ -1357,7 +1358,7 @@ class CachedLocalEngineArtifacts implements Artifacts { } @override - bool get isLocalEngine => true; + bool get usesLocalArtifacts => true; } class CachedLocalWebSdkArtifacts implements Artifacts { @@ -1559,7 +1560,7 @@ class CachedLocalWebSdkArtifacts implements Artifacts { } @override - bool get isLocalEngine => _parent.isLocalEngine; + bool get usesLocalArtifacts => true; @override LocalEngineInfo? get localEngineInfo => _parent.localEngineInfo; @@ -1620,7 +1621,7 @@ class OverrideArtifacts implements Artifacts { String getEngineType(TargetPlatform platform, [ BuildMode? mode ]) => parent.getEngineType(platform, mode); @override - bool get isLocalEngine => parent.isLocalEngine; + bool get usesLocalArtifacts => parent.usesLocalArtifacts; @override FileSystemEntity getHostArtifact(HostArtifact artifact) { @@ -1675,7 +1676,7 @@ class _TestArtifacts implements Artifacts { } @override - bool get isLocalEngine => false; + bool get usesLocalArtifacts => false; @override FileSystemEntity getHostArtifact(HostArtifact artifact) { @@ -1694,7 +1695,7 @@ class _TestLocalEngine extends _TestArtifacts { ); @override - bool get isLocalEngine => true; + bool get usesLocalArtifacts => true; @override final LocalEngineInfo localEngineInfo; diff --git a/packages/flutter_tools/lib/src/bundle_builder.dart b/packages/flutter_tools/lib/src/bundle_builder.dart index 83ebbcd73204c..effda740a514f 100644 --- a/packages/flutter_tools/lib/src/bundle_builder.dart +++ b/packages/flutter_tools/lib/src/bundle_builder.dart @@ -60,7 +60,7 @@ class BundleBuilder { buildDir: project.dartTool.childDirectory('flutter_build'), cacheDir: globals.cache.getRoot(), flutterRootDir: globals.fs.directory(Cache.flutterRoot), - engineVersion: globals.artifacts!.isLocalEngine + engineVersion: globals.artifacts!.usesLocalArtifacts ? null : globals.flutterVersion.engineRevision, defines: { diff --git a/packages/flutter_tools/lib/src/commands/assemble.dart b/packages/flutter_tools/lib/src/commands/assemble.dart index 42252acbee08a..1f2de22656329 100644 --- a/packages/flutter_tools/lib/src/commands/assemble.dart +++ b/packages/flutter_tools/lib/src/commands/assemble.dart @@ -248,7 +248,7 @@ class AssembleCommand extends FlutterCommand { usage: globals.flutterUsage, analytics: globals.analytics, platform: globals.platform, - engineVersion: artifacts.isLocalEngine + engineVersion: artifacts.usesLocalArtifacts ? null : globals.flutterVersion.engineRevision, generateDartPluginRegistry: true, diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart index fd6589397baef..fc424ca9e1207 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart @@ -150,7 +150,7 @@ abstract class BuildFrameworkCommand extends BuildSubCommand { ...framework.parent .listSync() .where((FileSystemEntity entity) => - entity.basename.endsWith('dSYM')) + entity.basename.endsWith('dSYM') && !entity.basename.startsWith('Flutter')) .map((FileSystemEntity entity) => ['-debug-symbols', entity.path]) .expand((List parameter) => parameter), ], @@ -456,7 +456,7 @@ end platform: globals.platform, usage: globals.flutterUsage, analytics: globals.analytics, - engineVersion: globals.artifacts!.isLocalEngine + engineVersion: globals.artifacts!.usesLocalArtifacts ? null : globals.flutterVersion.engineRevision, generateDartPluginRegistry: true, diff --git a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart index deebf44e4ba1d..7794b72860b67 100644 --- a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart @@ -237,7 +237,7 @@ end platform: globals.platform, usage: globals.flutterUsage, analytics: globals.analytics, - engineVersion: globals.artifacts!.isLocalEngine ? null : globals.flutterVersion.engineRevision, + engineVersion: globals.artifacts!.usesLocalArtifacts ? null : globals.flutterVersion.engineRevision, generateDartPluginRegistry: true, ); Target target; diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index 5aa0266327e5d..f0c2e4e2ccdc7 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -28,7 +28,7 @@ class BuildWebCommand extends BuildSubCommand { usesPubOption(); usesBuildNumberOption(); usesBuildNameOption(); - addBuildModeFlags(verboseHelp: verboseHelp, excludeDebug: true); + addBuildModeFlags(verboseHelp: verboseHelp); usesDartDefineOption(); addEnableExperimentation(hide: !verboseHelp); addNullSafetyModeOptions(hide: !verboseHelp); @@ -62,7 +62,6 @@ class BuildWebCommand extends BuildSubCommand { abbr: 'O', help: 'Sets the optimization level used for Dart compilation to JavaScript/Wasm.', - defaultsTo: '${WebCompilerConfig.kDefaultOptimizationLevel}', allowed: const ['0', '1', '2', '3', '4'], ); argParser.addFlag( @@ -134,10 +133,11 @@ class BuildWebCommand extends BuildSubCommand { throwToolExit('"build web" is not currently supported. To enable, run "flutter config --enable-web".'); } - final int optimizationLevel = int.parse(stringArg('optimization-level')!); + final String? optimizationLevelArg = stringArg('optimization-level'); + final int? optimizationLevel = optimizationLevelArg != null ? int.parse(optimizationLevelArg) : null; final String? dart2jsOptimizationLevelValue = stringArg('dart2js-optimization'); - final int jsOptimizationLevel = dart2jsOptimizationLevelValue != null + final int? jsOptimizationLevel = dart2jsOptimizationLevelValue != null ? int.parse(dart2jsOptimizationLevelValue.substring(1)) : optimizationLevel; @@ -191,9 +191,6 @@ class BuildWebCommand extends BuildSubCommand { final String target = stringArg('target')!; final BuildInfo buildInfo = await getBuildInfo(); - if (buildInfo.isDebug) { - throwToolExit('debug builds cannot be built directly for the web. Try using "flutter run"'); - } final String? baseHref = stringArg('base-href'); if (baseHref != null && !(baseHref.startsWith('/') && baseHref.endsWith('/'))) { throwToolExit( diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index 9ba518dcb38a9..9f8ae0246070c 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -142,6 +142,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { WindowsVersionValidator( operatingSystemUtils: globals.os, processLister: ProcessLister(globals.processManager), + versionExtractor: WindowsVersionExtractor( + processManager: globals.processManager, + logger: globals.logger, + ), ), if (androidWorkflow!.appliesToHostPlatform) GroupedValidator([androidValidator!, androidLicenseValidator!]), diff --git a/packages/flutter_tools/lib/src/flutter_plugins.dart b/packages/flutter_tools/lib/src/flutter_plugins.dart index 67e97990876e6..ec88c8db90168 100644 --- a/packages/flutter_tools/lib/src/flutter_plugins.dart +++ b/packages/flutter_tools/lib/src/flutter_plugins.dart @@ -1010,13 +1010,14 @@ Future refreshPluginsList( bool iosPlatform = false, bool macOSPlatform = false, bool forceCocoaPodsOnly = false, + bool writeLegacyPluginsList = true, }) async { final List plugins = await findPlugins(project); // Sort the plugins by name to keep ordering stable in generated files. plugins.sort((Plugin left, Plugin right) => left.name.compareTo(right.name)); - // TODO(franciscojma): Remove once migration is complete. + // TODO(matanlurey): Remove once migration is complete. // Write the legacy plugin files to avoid breaking existing apps. - final bool legacyChanged = _writeFlutterPluginsListLegacy(project, plugins); + final bool legacyChanged = writeLegacyPluginsList && _writeFlutterPluginsListLegacy(project, plugins); final bool changed = _writeFlutterPluginsList( project, @@ -1131,7 +1132,7 @@ Future injectPlugins( /// /// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`. bool hasPlugins(FlutterProject project) { - return _readFileContent(project.flutterPluginsFile) != null; + return _readFileContent(project.flutterPluginsDependenciesFile) != null; } /// Resolves the plugin implementations for all platforms. diff --git a/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart b/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart index eab5413263cb7..3ff219730e2fe 100644 --- a/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart +++ b/packages/flutter_tools/lib/src/macos/cocoapod_utils.dart @@ -31,6 +31,12 @@ Future processPodsIfNeeded( iosPlatform: project.ios.existsSync(), macOSPlatform: project.macos.existsSync(), forceCocoaPodsOnly: forceCocoaPodsOnly, + // TODO(matanlurey): As-per discussion on https://github.com/flutter/flutter/pull/157393 + // we'll assume that iOS/MacOS builds do not use or rely on the `.flutter-plugins` legacy + // file being generated. A better long-term fix would be not to have a call to refreshPluginsList + // at all, and instead have it implicitly run by the FlutterCommand instead. See + // https://github.com/flutter/flutter/issues/157391 for details. + writeLegacyPluginsList: false, ); // If there are no plugins and if the project is a not module with an existing diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index 1a7e2e5c2ce2a..382e18b271b10 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -467,7 +467,19 @@ class AndroidProject extends FlutterProjectPlatform { static final RegExp _androidNamespacePattern = RegExp('android {[\\S\\s]+namespace\\s*=?\\s*[\'"](.+)[\'"]'); static final RegExp _applicationIdPattern = RegExp('^\\s*applicationId\\s*=?\\s*[\'"](.*)[\'"]\\s*\$'); static final RegExp _imperativeKotlinPluginPattern = RegExp('^\\s*apply plugin\\:\\s+[\'"]kotlin-android[\'"]\\s*\$'); - static final RegExp _declarativeKotlinPluginPattern = RegExp('^\\s*id\\s+[\'"]kotlin-android[\'"]\\s*\$'); + + /// Examples of strings that this regex matches: + /// - `id "kotlin-android"` + /// - `id("kotlin-android")` + /// - `id ( "kotlin-android" ) ` + /// - `id "org.jetbrains.kotlin.android"` + /// - `id("org.jetbrains.kotlin.android")` + /// - `id ( "org.jetbrains.kotlin.android" )` + static final List _declarativeKotlinPluginPatterns = [ + RegExp('^\\s*id\\s*\\(?\\s*[\'"]kotlin-android[\'"]\\s*\\)?\\s*\$'), + RegExp('^\\s*id\\s*\\(?\\s*[\'"]org.jetbrains.kotlin.android[\'"]\\s*\\)?\\s*\$'), + ]; + /// Pattern used to find the assignment of the "group" property in Gradle. /// Expected example: `group "dev.flutter.plugin"` @@ -563,7 +575,9 @@ class AndroidProject extends FlutterProjectPlatform { /// True, if the app project is using Kotlin. bool get isKotlin { final bool imperativeMatch = firstMatchInFile(appGradleFile, _imperativeKotlinPluginPattern) != null; - final bool declarativeMatch = firstMatchInFile(appGradleFile, _declarativeKotlinPluginPattern) != null; + final bool declarativeMatch = _declarativeKotlinPluginPatterns.any((RegExp pattern) { + return (firstMatchInFile(appGradleFile, pattern) != null); + }); return imperativeMatch || declarativeMatch; } diff --git a/packages/flutter_tools/lib/src/project_validator.dart b/packages/flutter_tools/lib/src/project_validator.dart index 2c65fb6c8d141..3bb7961f852fc 100644 --- a/packages/flutter_tools/lib/src/project_validator.dart +++ b/packages/flutter_tools/lib/src/project_validator.dart @@ -2,17 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:collection'; - -import 'package:process/process.dart'; - import 'base/file_system.dart'; -import 'base/io.dart'; import 'base/logger.dart'; import 'base/platform.dart'; import 'cache.dart'; -import 'convert.dart'; -import 'dart_pub_json_formatter.dart'; import 'flutter_manifest.dart'; import 'project.dart'; import 'project_validator_result.dart'; @@ -211,74 +204,3 @@ class GeneralInfoProjectValidator extends ProjectValidator { @override String get title => 'General Info'; } - -class PubDependenciesProjectValidator extends ProjectValidator { - const PubDependenciesProjectValidator(this._processManager); - final ProcessManager _processManager; - - @override - Future> start(FlutterProject project) async { - const String name = 'Dart dependencies'; - final ProcessResult processResult = await _processManager.run(['dart', 'pub', 'deps', '--json']); - if (processResult.stdout is! String) { - return [ - _createProjectValidatorError(name, 'Command dart pub deps --json failed') - ]; - } - - final LinkedHashMap jsonResult; - final List result = []; - try { - jsonResult = json.decode( - processResult.stdout.toString() - ) as LinkedHashMap; - } on FormatException { - result.add(_createProjectValidatorError(name, processResult.stderr.toString())); - return result; - } - - final DartPubJson dartPubJson = DartPubJson(jsonResult); - final List dependencies = []; - - // Information retrieved from the pubspec.lock file if a dependency comes from - // the hosted url https://pub.dartlang.org we ignore it or if the package - // is the current directory being analyzed (root). - final Set hostedDependencies = {'hosted', 'root'}; - - for (final DartDependencyPackage package in dartPubJson.packages) { - if (!hostedDependencies.contains(package.source)) { - dependencies.addAll(package.dependencies); - } - } - - final String value; - if (dependencies.isNotEmpty) { - final String verb = dependencies.length == 1 ? 'is' : 'are'; - value = '${dependencies.join(', ')} $verb not hosted'; - } else { - value = 'All pub dependencies are hosted on https://pub.dartlang.org'; - } - - result.add( - ProjectValidatorResult( - name: name, - value: value, - status: StatusProjectValidator.info, - ) - ); - - return result; - } - - @override - bool supportsProject(FlutterProject project) { - return true; - } - - @override - String get title => 'Pub dependencies'; - - ProjectValidatorResult _createProjectValidatorError(String name, String value) { - return ProjectValidatorResult(name: name, value: value, status: StatusProjectValidator.error); - } -} diff --git a/packages/flutter_tools/lib/src/update_packages_pins.dart b/packages/flutter_tools/lib/src/update_packages_pins.dart index 950cb8b39c1cf..83e8acc01d9b2 100644 --- a/packages/flutter_tools/lib/src/update_packages_pins.dart +++ b/packages/flutter_tools/lib/src/update_packages_pins.dart @@ -26,9 +26,9 @@ const Map kManuallyPinnedDependencies = { 'native_assets_builder': '0.8.3', // Under active development with breaking changes until 1.0.0. Manually rolled by @dcharkes. 'native_assets_cli': '0.8.0', // Under active development with breaking changes until 1.0.0. Manually rolled by @dcharkes. 'material_color_utilities': '0.11.1', // Keep pinned to latest until 1.0.0. - 'leak_tracker': '10.0.7', // https://github.com/flutter/devtools/issues/3951 + 'leak_tracker': '10.0.8', // https://github.com/flutter/devtools/issues/3951 'leak_tracker_testing': '3.0.1', // https://github.com/flutter/devtools/issues/3951 - 'leak_tracker_flutter_testing': '3.0.8', // https://github.com/flutter/devtools/issues/3951 + 'leak_tracker_flutter_testing': '3.0.9', // https://github.com/flutter/devtools/issues/3951 }; /// These are packages that are explicitly excluded from appearing in the list diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index c7082cd66ad91..973ed7b9bb405 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -111,7 +111,7 @@ class WebBuilder { usage: _flutterUsage, analytics: _analytics, cacheDir: globals.cache.getRoot(), - engineVersion: globals.artifacts!.isLocalEngine ? null : _flutterVersion.engineRevision, + engineVersion: globals.artifacts!.usesLocalArtifacts ? null : _flutterVersion.engineRevision, flutterRootDir: _fileSystem.directory(Cache.flutterRoot), // Web uses a different Dart plugin registry. // https://github.com/flutter/flutter/issues/80406 diff --git a/packages/flutter_tools/lib/src/web/compiler_config.dart b/packages/flutter_tools/lib/src/web/compiler_config.dart index aa346df4fbecf..939b9b96ed3ee 100644 --- a/packages/flutter_tools/lib/src/web/compiler_config.dart +++ b/packages/flutter_tools/lib/src/web/compiler_config.dart @@ -13,22 +13,29 @@ enum CompileTarget { sealed class WebCompilerConfig { const WebCompilerConfig({required this.renderer, - required this.optimizationLevel, + this.optimizationLevel, required this.sourceMaps}); - - /// The default optimization level for dart2js/dart2wasm. - static const int kDefaultOptimizationLevel = 4; - /// Build environment flag for [optimizationLevel]. static const String kOptimizationLevel = 'OptimizationLevel'; /// Build environment flag for [sourceMaps]. static const String kSourceMapsEnabled = 'SourceMaps'; - /// The compiler optimization level. + /// Calculates the optimization level for dart2js/dart2wasm for the given + /// build mode. + int optimizationLevelForBuildMode(BuildMode mode) => + optimizationLevel ?? switch (mode) { + BuildMode.debug => 0, + BuildMode.profile || BuildMode.release => 4, + BuildMode.jitRelease => throw ArgumentError('Invalid build mode for web'), + }; + + /// The compiler optimization level specified by the user. /// /// Valid values are O0 (lowest, debug default) to O4 (highest, release default). - final int optimizationLevel; + /// If the value is null, the user hasn't specified an optimization level and an + /// appropriate default for the build mode will be used instead. + final int? optimizationLevel; /// `true` if the compiler build should output source maps. final bool sourceMaps; @@ -40,7 +47,7 @@ sealed class WebCompilerConfig { String get buildKey; Map get buildEventAnalyticsValues => { - 'optimizationLevel': optimizationLevel, + if (optimizationLevel != null) 'optimizationLevel': optimizationLevel!, }; @@ -56,7 +63,7 @@ class JsCompilerConfig extends WebCompilerConfig { this.csp = false, this.dumpInfo = false, this.nativeNullAssertions = false, - super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel, + super.optimizationLevel, this.noFrequencyBasedMinification = false, super.sourceMaps = true, super.renderer = WebRendererMode.defaultForJs, @@ -68,7 +75,6 @@ class JsCompilerConfig extends WebCompilerConfig { required WebRendererMode renderer, }) : this( nativeNullAssertions: nativeNullAssertions, - optimizationLevel: WebCompilerConfig.kDefaultOptimizationLevel , renderer: renderer, ); @@ -108,13 +114,22 @@ class JsCompilerConfig extends WebCompilerConfig { if (buildMode == BuildMode.debug) '--enable-asserts', ]; + @override + int optimizationLevelForBuildMode(BuildMode mode) { + final int level = super.optimizationLevelForBuildMode(mode); + + // dart2js optimization level 0 is not well supported. Use + // 1 instead. + return level == 0 ? 1 : level; + } + /// Arguments to use in the full JS compile, but not CFE-only. /// /// Includes the contents of [toSharedCommandOptions]. List toCommandOptions(BuildMode buildMode) => [ if (buildMode != BuildMode.release) '--no-minify', ...toSharedCommandOptions(buildMode), - '-O$optimizationLevel', + '-O${optimizationLevelForBuildMode(buildMode)}', if (dumpInfo) '--stage=dump-info-all', if (noFrequencyBasedMinification) '--no-frequency-based-minification', if (csp) '--csp', @@ -137,7 +152,7 @@ class JsCompilerConfig extends WebCompilerConfig { /// Configuration for the Wasm compiler. class WasmCompilerConfig extends WebCompilerConfig { const WasmCompilerConfig({ - super.optimizationLevel = WebCompilerConfig.kDefaultOptimizationLevel, + super.optimizationLevel, this.stripWasm = true, super.sourceMaps = true, super.renderer = WebRendererMode.defaultForWasm, @@ -155,7 +170,7 @@ class WasmCompilerConfig extends WebCompilerConfig { List toCommandOptions(BuildMode buildMode) { final bool stripSymbols = buildMode == BuildMode.release && stripWasm; return [ - '-O$optimizationLevel', + '-O${optimizationLevelForBuildMode(buildMode)}', '--${stripSymbols ? '' : 'no-'}strip-wasm', if (!sourceMaps) '--no-source-maps', if (buildMode == BuildMode.debug) '--extra-compiler-option=--enable-asserts', diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index ef729796b484f..5be4a4e70865b 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart @@ -160,7 +160,9 @@ abstract class ChromiumDevice extends Device { ApplicationPackage? app, { String? userIdentifier, }) async { - await _chrome?.close(); + final Future? future = _chrome?.close(); + _chrome = null; + await future; return true; } diff --git a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart index afe16a66794ac..93bcf6bad3704 100644 --- a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart +++ b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart @@ -5,6 +5,7 @@ import 'package:process/process.dart'; import '../base/io.dart'; +import '../base/logger.dart'; import '../base/os.dart'; import '../doctor_validator.dart'; @@ -16,8 +17,8 @@ const List kUnsupportedVersions = [ ]; /// Regex pattern for identifying line from systeminfo stdout with windows version -/// (ie. 10.5.4123) -const String kWindowsOSVersionSemVerPattern = r'([0-9]+)\.([0-9]+)\.([0-9\.]+)'; +/// (ie. 10.0.22631.4037) +const String kWindowsOSVersionSemVerPattern = r'([0-9]+)\.([0-9]+)\.([0-9]+)\.?([0-9\.]+)?'; /// Regex pattern for identifying a running instance of the Topaz OFD process. /// This is a known process that interferes with the build toolchain. @@ -29,12 +30,18 @@ class WindowsVersionValidator extends DoctorValidator { const WindowsVersionValidator({ required OperatingSystemUtils operatingSystemUtils, required ProcessLister processLister, + required WindowsVersionExtractor versionExtractor, }) : _operatingSystemUtils = operatingSystemUtils, _processLister = processLister, + _versionExtractor = versionExtractor, super('Windows Version'); + // See https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information + static const int _lowestWindows11BuildNumber = 22000; + final OperatingSystemUtils _operatingSystemUtils; final ProcessLister _processLister; + final WindowsVersionExtractor _versionExtractor; Future _topazScan() async { if (!_processLister.canRunPowershell()) { @@ -86,7 +93,18 @@ class WindowsVersionValidator extends DoctorValidator { if (matches.length == 1 && !kUnsupportedVersions.contains(matches.elementAt(0).group(1))) { windowsVersionStatus = ValidationType.success; - statusInfo = 'Installed version of Windows is version 10 or higher'; + final WindowsVersionExtractionResult details = await _versionExtractor.getDetails(); + String? caption = details.caption; + if (caption == null || caption.isEmpty) { + final bool isWindows11 = int.parse(matches.elementAt(0).group(3)!) > _lowestWindows11BuildNumber; + if (isWindows11) { + caption = 'Windows 11 or higher'; + } else { + caption = 'Windows 10'; + } + } + statusInfo = [caption, details.displayVersion, details.releaseId] + .where((String? part) => part != null).join(', '); // Check if the Topaz OFD security module is running, and warn the user if it is. // See https://github.com/flutter/flutter/issues/121366 @@ -138,3 +156,88 @@ class ProcessLister { throw StateError('Failed to find $powershell or $pwsh on PATH'); } } + +/// This helper class takes the Windows edition and processor architecture from the Windows management interface (WMI) +/// with the wmic command. The Windows ReleaseId and DisplayVersion are taken from the registry key +/// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion via the reg command. The extracted data are caption (consists of +/// the edition and the processor architecture), releaseId and displayVersion and are returned via the +/// [WindowsVersionExtractionResult] class. +class WindowsVersionExtractor { + WindowsVersionExtractor({ + required ProcessManager processManager, + required Logger logger, + }) : _logger = logger, _processManager = processManager; + + final ProcessManager _processManager; + final Logger _logger; + + Future getDetails() async { + String? caption, releaseId, displayVersion; + try { + final ProcessResult captionResult = await _processManager.run( + ['wmic', 'os', 'get', 'Caption,OSArchitecture'], + ); + + if (captionResult.exitCode == 0) { + final String? output = captionResult.stdout as String?; + if (output != null) { + final List parts = output.split('\n'); + if (parts.length >= 2) { + caption = parts[1].replaceAll('Microsoft Windows', '').replaceAll(' ', ' ').trim(); + } + } + } + } on ProcessException catch (e) { + _logger.printTrace('Failed to get Caption and OSArchitecture from WMI: $e'); + } + + try { + final ProcessResult osDetails = await _processManager.run( + ['reg', 'query', r'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', '/t', 'REG_SZ'], + ); + + if (osDetails.exitCode == 0) { + final String? output = osDetails.stdout as String?; + if (output != null) { + final Map data = Map.fromEntries( + output.split('\n').where((String line) => line.contains('REG_SZ')).map((String line) { + final List parts = line.split('REG_SZ'); + return MapEntry(parts.first.trim(), parts.last.trim()); + })); + releaseId = data['ReleaseId']; + displayVersion = data['DisplayVersion']; + } + } + } on ProcessException catch (e) { + _logger.printTrace('Failed to get ReleaseId and DisplayVersion from registry: $e'); + } + + return WindowsVersionExtractionResult( + caption: caption, + releaseId: releaseId, + displayVersion: displayVersion, + ); + } +} + +/// The result of the Windows version extraction. Typically values would be for [caption] e.g. "11 Pro 64-bit", for +/// [releaseId] e.g. "2009" and for [displayVersion] e.g. "22H2". All values could be null when the extraction fails. +final class WindowsVersionExtractionResult { + WindowsVersionExtractionResult({ + required this.caption, + required this.releaseId, + required this.displayVersion, + }); + + factory WindowsVersionExtractionResult.empty() { + return WindowsVersionExtractionResult( + caption: null, + releaseId: null, + displayVersion: null, + ); + } + + final String? caption; + final String? releaseId; + final String? displayVersion; +} diff --git a/packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.tmpl b/packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.kts.tmpl similarity index 84% rename from packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.tmpl rename to packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.kts.tmpl index 6d7ad78b1d7d9..15700aaa436d8 100644 --- a/packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.tmpl +++ b/packages/flutter_tools/templates/app_shared/android-java.tmpl/app/build.gradle.kts.tmpl @@ -1,8 +1,8 @@ plugins { - id "com.android.application" - id "kotlin-android" + id("com.android.application") + id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" + id("dev.flutter.flutter-gradle-plugin") } android { @@ -16,7 +16,7 @@ android { } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = JavaVersion.VERSION_1_8.toString() } defaultConfig { @@ -34,7 +34,7 @@ android { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug + signingConfig = signingConfigs.getByName("debug") } } } diff --git a/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.kts.tmpl b/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.kts.tmpl new file mode 100644 index 0000000000000..ffa69a07cfe76 --- /dev/null +++ b/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.kts.tmpl @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = file("../build") +subprojects { + project.buildDir = file("${rootProject.buildDir}/${project.name}") +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.buildDir) +} diff --git a/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.tmpl deleted file mode 100644 index d2ffbffa4cd25..0000000000000 --- a/packages/flutter_tools/templates/app_shared/android-java.tmpl/build.gradle.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.tmpl b/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.kts.tmpl similarity index 84% rename from packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.tmpl rename to packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.kts.tmpl index 6d7ad78b1d7d9..15700aaa436d8 100644 --- a/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.tmpl +++ b/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/app/build.gradle.kts.tmpl @@ -1,8 +1,8 @@ plugins { - id "com.android.application" - id "kotlin-android" + id("com.android.application") + id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. - id "dev.flutter.flutter-gradle-plugin" + id("dev.flutter.flutter-gradle-plugin") } android { @@ -16,7 +16,7 @@ android { } kotlinOptions { - jvmTarget = JavaVersion.VERSION_1_8 + jvmTarget = JavaVersion.VERSION_1_8.toString() } defaultConfig { @@ -34,7 +34,7 @@ android { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig = signingConfigs.debug + signingConfig = signingConfigs.getByName("debug") } } } diff --git a/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.kts.tmpl b/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.kts.tmpl new file mode 100644 index 0000000000000..ffa69a07cfe76 --- /dev/null +++ b/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.kts.tmpl @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = file("../build") +subprojects { + project.buildDir = file("${rootProject.buildDir}/${project.name}") +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.buildDir) +} diff --git a/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.tmpl b/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.tmpl deleted file mode 100644 index d2ffbffa4cd25..0000000000000 --- a/packages/flutter_tools/templates/app_shared/android-kotlin.tmpl/build.gradle.tmpl +++ /dev/null @@ -1,18 +0,0 @@ -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = "../build" -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(":app") -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.kts.tmpl b/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.kts.tmpl new file mode 100644 index 0000000000000..d0d44f3349620 --- /dev/null +++ b/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.kts.tmpl @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "{{agpVersion}}" apply false + id("org.jetbrains.kotlin.android") version "{{kotlinVersion}}" apply false +} + +include(":app") diff --git a/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.tmpl b/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.tmpl deleted file mode 100644 index f1272c957ca0f..0000000000000 --- a/packages/flutter_tools/templates/app_shared/android.tmpl/settings.gradle.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - }() - - includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - - repositories { - google() - mavenCentral() - gradlePluginPortal() - } -} - -plugins { - id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "{{agpVersion}}" apply false - id "org.jetbrains.kotlin.android" version "{{kotlinVersion}}" apply false -} - -include ":app" diff --git a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl index b5635ff6988b2..b7f06a1536071 100644 --- a/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl +++ b/packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/AndroidManifest.xml.tmpl @@ -19,13 +19,6 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize"> - - diff --git a/packages/flutter_tools/templates/template_manifest.json b/packages/flutter_tools/templates/template_manifest.json index 98d9d70bc8bb0..857c6dcc3eea7 100644 --- a/packages/flutter_tools/templates/template_manifest.json +++ b/packages/flutter_tools/templates/template_manifest.json @@ -14,13 +14,13 @@ "templates/app_shared/.idea/workspace.xml.tmpl", "templates/app_shared/.metadata.tmpl", "templates/app_shared/analysis_options.yaml.tmpl", - "templates/app_shared/android-java.tmpl/app/build.gradle.tmpl", + "templates/app_shared/android-java.tmpl/app/build.gradle.kts.tmpl", "templates/app_shared/android-java.tmpl/app/src/main/java/androidIdentifier/MainActivity.java.tmpl", - "templates/app_shared/android-java.tmpl/build.gradle.tmpl", + "templates/app_shared/android-java.tmpl/build.gradle.kts.tmpl", "templates/app_shared/android-java.tmpl/projectName_android.iml.tmpl", - "templates/app_shared/android-kotlin.tmpl/app/build.gradle.tmpl", + "templates/app_shared/android-kotlin.tmpl/app/build.gradle.kts.tmpl", "templates/app_shared/android-kotlin.tmpl/app/src/main/kotlin/androidIdentifier/MainActivity.kt.tmpl", - "templates/app_shared/android-kotlin.tmpl/build.gradle.tmpl", + "templates/app_shared/android-kotlin.tmpl/build.gradle.kts.tmpl", "templates/app_shared/android-kotlin.tmpl/projectName_android.iml.tmpl", "templates/app_shared/android.tmpl/.gitignore", "templates/app_shared/android.tmpl/app/src/debug/AndroidManifest.xml.tmpl", @@ -36,7 +36,7 @@ "templates/app_shared/android.tmpl/app/src/main/res/values/styles.xml", "templates/app_shared/android.tmpl/app/src/profile/AndroidManifest.xml.tmpl", "templates/app_shared/android.tmpl/gradle.properties.tmpl", - "templates/app_shared/android.tmpl/settings.gradle.tmpl", + "templates/app_shared/android.tmpl/settings.gradle.kts.tmpl", "templates/app_shared/android.tmpl/gradle/wrapper/gradle-wrapper.properties.tmpl", "templates/app_shared/android.tmpl/settings.gradle", "templates/app_shared/ios-objc.tmpl/Runner.xcodeproj/project.pbxproj.tmpl", diff --git a/packages/flutter_tools/test/android_preview_integration.shard/flutter_build_preview_sdk_test.dart b/packages/flutter_tools/test/android_preview_integration.shard/flutter_build_preview_sdk_test.dart index 56b540b2f3aca..7e9c1723d9d70 100644 --- a/packages/flutter_tools/test/android_preview_integration.shard/flutter_build_preview_sdk_test.dart +++ b/packages/flutter_tools/test/android_preview_integration.shard/flutter_build_preview_sdk_test.dart @@ -44,7 +44,7 @@ void main() { test( 'build succeeds targeting string compileSdkVersion', () async { - final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); + final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); // write a build.gradle with compileSdkVersion as `android-UpsideDownCake` which is a string preview version buildGradleFile.writeAsStringSync( buildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkVersion = "android-UpsideDownCake"'), @@ -72,7 +72,7 @@ void main() { test( 'build succeeds targeting string compileSdkPreview', () async { - final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); + final File buildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); // write a build.gradle with compileSdkPreview as `UpsideDownCake` which is a string preview version buildGradleFile.writeAsStringSync( buildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkPreview = "UpsideDownCake"'), @@ -100,7 +100,7 @@ void main() { test( 'build succeeds when both example app and plugin target compileSdkPreview', () async { - final File appBuildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); + final File appBuildGradleFile = exampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); // write a build.gradle with compileSdkPreview as `UpsideDownCake` which is a string preview version appBuildGradleFile.writeAsStringSync( appBuildGradleFile.readAsStringSync().replaceFirst(compileSdkVersionMatch, 'compileSdkPreview = "UpsideDownCake"'), diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart index 605e1dff7e2b5..1ee8e0a073f41 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_darwin_framework_test.dart @@ -558,6 +558,8 @@ void main() { final Directory parentA = fileSystem.directory('FrameworkA')..createSync(); final File dSYMA = parentA.childFile('FrameworkA.framework.dSYM')..createSync(); final Directory frameworkA = parentA.childDirectory('FrameworkA.framework')..createSync(); + // Flutter.framework.dSYM should be correctly filtered out. + parentA.childFile('Flutter.framework.dSYM').createSync(); final Directory parentB = fileSystem.directory('FrameworkB')..createSync(); final File dSYMB = parentB.childFile('FrameworkB.framework.dSYM')..createSync(); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart index 4d6e1f30e1b86..c7aa4d5a60e96 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart @@ -83,25 +83,6 @@ void main() { ProcessManager: () => processManager, }); - testUsingContext('Refuses to build a debug build for web', () async { - final CommandRunner runner = createTestCommandRunner(BuildCommand( - artifacts: artifacts, - androidSdk: FakeAndroidSdk(), - buildSystem: TestBuildSystem.all(BuildResult(success: true)), - fileSystem: fileSystem, - logger: logger, - processUtils: processUtils, - osUtils: FakeOperatingSystemUtils(), - )); - - expect(() => runner.run(['build', 'web', '--debug', '--no-pub']), - throwsA(isA())); - }, overrides: { - Platform: () => fakePlatform, - FeatureFlags: () => TestFeatureFlags(isWebEnabled: true), - ProcessManager: () => processManager, - }); - testUsingContext('Refuses to build for web when feature is disabled', () async { final CommandRunner runner = createTestCommandRunner(BuildCommand( artifacts: artifacts, 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 acfb0e6ff183d..a59303fadc937 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -3126,9 +3126,9 @@ void main() { await runner.run(['create', '--no-pub', projectDir.path]); - expect(globals.fs.isFileSync('${projectDir.path}/android/app/build.gradle'), true); + expect(globals.fs.isFileSync('${projectDir.path}/android/app/build.gradle.kts'), true); - final String buildContent = await globals.fs.file('${projectDir.path}/android/app/build.gradle').readAsString(); + final String buildContent = await globals.fs.file('${projectDir.path}/android/app/build.gradle.kts').readAsString(); expect(buildContent.contains('compileSdk = flutter.compileSdkVersion'), true); expect(buildContent.contains('ndkVersion = flutter.ndkVersion'), true); diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart index 39c91bb793b0f..2a5f67f2559fb 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -838,17 +838,4 @@ flutter: ); }); }); - - test('Current settings.gradle is in our legacy settings.gradle file set', () { - // If this test fails, you probably edited templates/app/android.tmpl. - // That's fine, but you now need to add a copy of that file to gradle/settings.gradle.legacy_versions, separated - // from the previous versions by a line that just says ";EOF". - final File templateSettingsDotGradle = globals.fs.file(globals.fs.path.join(Cache.flutterRoot!, 'packages', 'flutter_tools', 'templates', 'app', 'android.tmpl', 'settings.gradle')); - final File legacySettingsDotGradleFiles = globals.fs.file(globals.fs.path.join(Cache.flutterRoot!, 'packages','flutter_tools', 'gradle', 'settings.gradle.legacy_versions')); - expect( - legacySettingsDotGradleFiles.readAsStringSync().split(';EOF').map((String body) => body.trim()), - contains(templateSettingsDotGradle.readAsStringSync().trim()), - ); - // TODO(zanderso): This is an integration test and should be moved to the integration shard. - }, skip: true); // https://github.com/flutter/flutter/issues/87922 } diff --git a/packages/flutter_tools/test/general.shard/artifacts_test.dart b/packages/flutter_tools/test/general.shard/artifacts_test.dart index 89734310ee07e..43a00db2b1315 100644 --- a/packages/flutter_tools/test/general.shard/artifacts_test.dart +++ b/packages/flutter_tools/test/general.shard/artifacts_test.dart @@ -300,6 +300,17 @@ void main() { 'darwin-x64', ); }); + + testWithoutContext('CachedLocalWebSdkArtifacts wrapping a versioned engine sets usesLocalArtifacts', () { + final CachedLocalWebSdkArtifacts webArtifacts = CachedLocalWebSdkArtifacts( + parent: artifacts, + webSdkPath: fileSystem.path.join(fileSystem.currentDirectory.path, 'out', 'wasm_release'), + fileSystem: fileSystem, + platform: platform, + operatingSystemUtils: FakeOperatingSystemUtils() + ); + expect(webArtifacts.usesLocalArtifacts, true); + }); }); group('LocalEngineArtifacts', () { diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart index 56ff685c755e1..9c4d3125ff8c5 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart @@ -909,7 +909,7 @@ void main() { '--no-minify', '--no-source-maps', '--enable-asserts', - '-O4', + '-O1', '-o', environment.buildDir.childFile('main.dart.js').absolute.path, environment.buildDir.childFile('app.dill').absolute.path, diff --git a/packages/flutter_tools/test/general.shard/dap/mocks.dart b/packages/flutter_tools/test/general.shard/dap/mocks.dart index 3b9eaf57a5c65..5a197c6a5d1b3 100644 --- a/packages/flutter_tools/test/general.shard/dap/mocks.dart +++ b/packages/flutter_tools/test/general.shard/dap/mocks.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:collection/collection.dart'; import 'package:dds/dap.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/platform.dart'; @@ -90,7 +89,7 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter { /// by the debug adapter. List get dapToFlutterRequests => dapToFlutterMessages .map((Map message) => message['method'] as String?) - .whereNotNull() + .nonNulls .toList(); /// A handler for the 'app.exposeUrl' reverse-request. diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart index 808f3eec8676b..b884cdbe0ab0e 100644 --- a/packages/flutter_tools/test/general.shard/plugins_test.dart +++ b/packages/flutter_tools/test/general.shard/plugins_test.dart @@ -448,6 +448,20 @@ dependencies: ProcessManager: () => FakeProcessManager.any(), }); + testUsingContext('Opting out of writeLegacyPluginsList omits .flutter-plugins', () async { + createFakePlugins(fs, [ + 'plugin_d', + 'plugin_a', + '/local_plugins/plugin_c', + '/local_plugins/plugin_b', + ]); + + await refreshPluginsList(flutterProject, writeLegacyPluginsList: false); + + expect(flutterProject.flutterPluginsFile, isNot(exists)); + expect(flutterProject.flutterPluginsDependenciesFile, exists); + }); + testUsingContext( 'Refreshing the plugin list modifies .flutter-plugins ' 'and .flutter-plugins-dependencies when there are plugins', () async { diff --git a/packages/flutter_tools/test/general.shard/pub_dependencies_project_validator_test.dart b/packages/flutter_tools/test/general.shard/pub_dependencies_project_validator_test.dart deleted file mode 100644 index 546e92d80a94a..0000000000000 --- a/packages/flutter_tools/test/general.shard/pub_dependencies_project_validator_test.dart +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:file/memory.dart'; -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/project.dart'; -import 'package:flutter_tools/src/project_validator.dart'; -import 'package:flutter_tools/src/project_validator_result.dart'; - -import '../src/common.dart'; -import '../src/context.dart'; - -void main() { - late FileSystem fileSystem; - - group('PubDependenciesProjectValidator', () { - - setUp(() { - fileSystem = MemoryFileSystem.test(); - }); - - testWithoutContext('success when all dependencies are hosted', () async { - final ProcessManager processManager = FakeProcessManager.list([ - const FakeCommand( - command: ['dart', 'pub', 'deps', '--json'], - stdout: '{"packages": [{"dependencies": ["abc"], "source": "hosted"}]}', - ), - ]); - final PubDependenciesProjectValidator validator = PubDependenciesProjectValidator(processManager); - - final List result = await validator.start( - FlutterProject.fromDirectoryTest(fileSystem.currentDirectory) - ); - const String expected = 'All pub dependencies are hosted on https://pub.dartlang.org'; - expect(result.length, 1); - expect(result[0].value, expected); - expect(result[0].status, StatusProjectValidator.info); - }); - - testWithoutContext('error when command dart pub deps fails', () async { - final ProcessManager processManager = FakeProcessManager.list([ - const FakeCommand( - command: ['dart', 'pub', 'deps', '--json'], - stderr: 'command fail', - ), - ]); - final PubDependenciesProjectValidator validator = PubDependenciesProjectValidator(processManager); - - final List result = await validator.start( - FlutterProject.fromDirectoryTest(fileSystem.currentDirectory) - ); - const String expected = 'command fail'; - expect(result.length, 1); - expect(result[0].value, expected); - expect(result[0].status, StatusProjectValidator.error); - }); - - testWithoutContext('info on dependencies not hosted', () async { - final ProcessManager processManager = FakeProcessManager.list([ - const FakeCommand( - command: ['dart', 'pub', 'deps', '--json'], - stdout: '{"packages": [{"dependencies": ["dep1", "dep2"], "source": "other"}]}', - ), - ]); - final PubDependenciesProjectValidator validator = PubDependenciesProjectValidator(processManager); - - final List result = await validator.start( - FlutterProject.fromDirectoryTest(fileSystem.currentDirectory) - ); - const String expected = 'dep1, dep2 are not hosted'; - expect(result.length, 1); - expect(result[0].value, expected); - expect(result[0].status, StatusProjectValidator.info); - }); - }); -} diff --git a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart index 57015d5bf4ffb..f8547264729e1 100644 --- a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart @@ -107,7 +107,7 @@ void main() { label: 'web-compile', parameters: CustomDimensions( buildEventSettings: - 'optimizationLevel: 4; web-renderer: skwasm,canvaskit; web-target: wasm,js;', + 'optimizationLevel: 0; web-renderer: skwasm,canvaskit; web-target: wasm,js;', ), ), @@ -121,7 +121,7 @@ void main() { Event.flutterBuildInfo( label: 'web-compile', buildType: 'web', - settings: 'optimizationLevel: 4; web-renderer: skwasm,canvaskit; web-target: wasm,js;', + settings: 'optimizationLevel: 0; web-renderer: skwasm,canvaskit; web-target: wasm,js;', ), ]), ); diff --git a/packages/flutter_tools/test/general.shard/web/devices_test.dart b/packages/flutter_tools/test/general.shard/web/devices_test.dart index 06f4d0cfc76b6..b33512364080d 100644 --- a/packages/flutter_tools/test/general.shard/web/devices_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devices_test.dart @@ -12,6 +12,7 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/web/chrome.dart'; import 'package:flutter_tools/src/web/web_device.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/fake_process_manager.dart'; @@ -32,6 +33,29 @@ void main() { expect(await webDevices.pollingGetDevices(), isEmpty); }); + testWithoutContext('Successive calls of ChromiumDevice.stopApp() do not try to close chrome', () async { + final TestChromiumLauncher launcher = TestChromiumLauncher( + launcher: () => _OnceClosableChromium(), + ); + + final _FakeChromiumDevice chromiumDevice = _FakeChromiumDevice( + chromiumLauncher: launcher, + fileSystem: MemoryFileSystem.test(), + logger: BufferLogger.test(), + ); + + await chromiumDevice.startApp( + null, + debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), + platformArgs: { + 'uri': '/', + } + ); + + await chromiumDevice.stopApp(null); + await chromiumDevice.stopApp(null); + }); + testWithoutContext('GoogleChromeDevice defaults', () async { final TestChromiumLauncher launcher = TestChromiumLauncher(); @@ -392,7 +416,11 @@ void main() { /// A test implementation of the [ChromiumLauncher] that launches a fixed instance. class TestChromiumLauncher implements ChromiumLauncher { - TestChromiumLauncher(); + TestChromiumLauncher({Chromium Function()? launcher}) { + _launcher = launcher ?? () => _UnimplementedChromium(); + } + + late Chromium Function() _launcher; @override Completer currentCompleter = Completer(); @@ -422,6 +450,7 @@ class TestChromiumLauncher implements ChromiumLauncher { Directory? cacheDir, List webBrowserFlags = const [], }) async { + currentCompleter.complete(_launcher()); return currentCompleter.future; } @@ -430,3 +459,34 @@ class TestChromiumLauncher implements ChromiumLauncher { return currentCompleter.future; } } + +class _FakeChromiumDevice extends ChromiumDevice { + _FakeChromiumDevice( + {required ChromiumLauncher chromiumLauncher, + required super.fileSystem, + required super.logger}) + : super( + name: 'fake name', + chromeLauncher: chromiumLauncher, + ); + + @override + Future get sdkNameAndVersion async => 'fake sdkNameAndVersion'; + + @override + String get name => 'fake name'; +} + +class _OnceClosableChromium extends Fake implements Chromium { + bool _closed = false; + + @override + Future close() async { + if (_closed) { + throw Exception('Already closed'); + } + _closed = true; + } +} + +class _UnimplementedChromium extends Fake implements Chromium { } diff --git a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart index 8f54cc9af24bf..bc19a694d8de4 100644 --- a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/windows/windows_version_validator.dart'; @@ -10,6 +11,7 @@ import 'package:test/fake.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/fake_process_manager.dart'; /// Fake [_WindowsUtils] to use for testing class FakeValidOperatingSystemUtils extends Fake @@ -58,10 +60,10 @@ FakeProcessLister powershellUnavailable() { /// The expected validation result object for /// a passing windows version test -const ValidationResult validWindows10ValidationResult = ValidationResult( +const ValidationResult validWindows11ValidationResult = ValidationResult( ValidationType.success, [], - statusInfo: 'Installed version of Windows is version 10 or higher', + statusInfo: '11 Pro 64-bit, 23H2, 2009', ); /// The expected validation result object for @@ -100,19 +102,37 @@ const ValidationResult getProcessFailed = ValidationResult( statusInfo: 'Problem detected with Windows installation', ); +class FakeVersionExtractor extends Fake implements WindowsVersionExtractor { + FakeVersionExtractor({required this.mockData}); + FakeVersionExtractor.win11ProX64() + : this( + mockData: WindowsVersionExtractionResult( + caption: '11 Pro 64-bit', + releaseId: '2009', + displayVersion: '23H2'), + ); + final WindowsVersionExtractionResult mockData; + + @override + Future getDetails() async { + return mockData; + } +} + void main() { testWithoutContext('Successfully running windows version check on windows 10', () async { final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), - processLister: ofdNotRunning()); + processLister: ofdNotRunning(), + versionExtractor: FakeVersionExtractor.win11ProX64()); final ValidationResult result = await windowsVersionValidator.validate(); - expect(result.type, validWindows10ValidationResult.type, + expect(result.type, validWindows11ValidationResult.type, reason: 'The ValidationResult type should be the same (installed)'); - expect(result.statusInfo, validWindows10ValidationResult.statusInfo, + expect(result.statusInfo, validWindows11ValidationResult.statusInfo, reason: 'The ValidationResult statusInfo messages should be the same'); }); @@ -123,13 +143,14 @@ void main() { WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( 'Microsoft Windows [versΓ£o 10.0.22621.1105]'), - processLister: ofdNotRunning()); + processLister: ofdNotRunning(), + versionExtractor: FakeVersionExtractor.win11ProX64()); final ValidationResult result = await windowsVersionValidator.validate(); - expect(result.type, validWindows10ValidationResult.type, + expect(result.type, validWindows11ValidationResult.type, reason: 'The ValidationResult type should be the same (installed)'); - expect(result.statusInfo, validWindows10ValidationResult.statusInfo, + expect(result.statusInfo, validWindows11ValidationResult.statusInfo, reason: 'The ValidationResult statusInfo messages should be the same'); }); @@ -138,7 +159,8 @@ void main() { WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( 'Microsoft Windows [Version 8.0.22621.1105]'), - processLister: ofdNotRunning()); + processLister: ofdNotRunning(), + versionExtractor: FakeVersionExtractor.win11ProX64()); final ValidationResult result = await windowsVersionValidator.validate(); @@ -172,7 +194,8 @@ OS η‰ˆζœ¬: 10.0.22621 ζš‚ηΌΊ Build 22621 final WindowsVersionValidator validator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), - processLister: ofdRunning()); + processLister: ofdRunning(), + versionExtractor: FakeVersionExtractor.win11ProX64()); final ValidationResult result = await validator.validate(); expect(result.type, ofdFoundRunning.type, reason: 'The ValidationResult type should be the same (partial)'); expect(result.statusInfo, ofdFoundRunning.statusInfo, reason: 'The ValidationResult statusInfo should be the same'); @@ -184,7 +207,8 @@ OS η‰ˆζœ¬: 10.0.22621 ζš‚ηΌΊ Build 22621 final WindowsVersionValidator validator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), - processLister: powershellUnavailable()); + processLister: powershellUnavailable(), + versionExtractor: FakeVersionExtractor.win11ProX64()); final ValidationResult result = await validator.validate(); expect(result.type, powershellUnavailableResult.type, reason: 'The ValidationResult type should be the same (partial)'); expect(result.statusInfo, powershellUnavailableResult.statusInfo, reason: 'The ValidationResult statusInfo should be the same'); @@ -196,7 +220,8 @@ OS η‰ˆζœ¬: 10.0.22621 ζš‚ηΌΊ Build 22621 final WindowsVersionValidator validator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), - processLister: failure()); + processLister: failure(), + versionExtractor: FakeVersionExtractor(mockData: WindowsVersionExtractionResult.empty())); final ValidationResult result = await validator.validate(); expect(result.type, getProcessFailed.type, reason: 'The ValidationResult type should be the same (partial)'); expect(result.statusInfo, getProcessFailed.statusInfo, reason: 'The ValidationResult statusInfo should be the same'); @@ -274,4 +299,173 @@ OS η‰ˆζœ¬: 10.0.22621 ζš‚ηΌΊ Build 22621 fail('Unexpected exception: $e'); } }); + + testWithoutContext('Parses Caption, OSArchitecture, releaseId, and CurrentVersion from the OS', () async { + final FakeProcessManager processManager = FakeProcessManager.list( + [ + const FakeCommand( + command: ['wmic', 'os', 'get', 'Caption,OSArchitecture'], + stdout: ''' +Caption OSArchitecture +Microsoft Windows 10 Enterprise 64-bit +'''), + const FakeCommand(command: [ + 'reg', + 'query', + r'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', + '/t', + 'REG_SZ', + ], stdout: r''' +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion + SystemRoot REG_SZ C:\Windows + BuildBranch REG_SZ vb_release + BuildGUID REG_SZ ffffffff-ffff-ffff-ffff-ffffffffffff + BuildLab REG_SZ 19041.vb_release.191206-1406 + BuildLabEx REG_SZ 19041.1.amd64fre.vb_release.191206-1406 + CompositionEditionID REG_SZ Enterprise + CurrentBuild REG_SZ 19045 + CurrentBuildNumber REG_SZ 19045 + CurrentType REG_SZ Multiprocessor Free + CurrentVersion REG_SZ 6.3 + EditionID REG_SZ Enterprise + EditionSubManufacturer REG_SZ + EditionSubstring REG_SZ + EditionSubVersion REG_SZ + InstallationType REG_SZ Client + ProductName REG_SZ Windows 10 Enterprise + ReleaseId REG_SZ 2009 + SoftwareType REG_SZ System + PathName REG_SZ C:\Windows + ProductId REG_SZ 00329-00000-00003-AA153 + DisplayVersion REG_SZ 22H2 + WinREVersion REG_SZ 10.0.19041.3920 + +End of search: 22 match(es) found. + +'''), + ], + ); + final WindowsVersionValidator validator = WindowsVersionValidator( + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: ofdNotRunning(), + versionExtractor: WindowsVersionExtractor( + processManager: processManager, + logger: BufferLogger.test(), + ), + ); + final ValidationResult result = await validator.validate(); + expect(result.type, ValidationType.success); + expect(result.statusInfo, '10 Enterprise 64-bit, 22H2, 2009'); + }); + + testWithoutContext('Differentiates Windows 11 from 10 when wmic call fails', () async { + const String windows10RegQueryOutput = r''' +HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion + SystemRoot REG_SZ C:\WINDOWS + BuildBranch REG_SZ ni_release + BuildGUID REG_SZ ffffffff-ffff-ffff-ffff-ffffffffffff + BuildLab REG_SZ 22621.ni_release.220506-1250 + BuildLabEx REG_SZ 22621.1.amd64fre.ni_release.220506-1250 + CompositionEditionID REG_SZ Enterprise + CurrentBuild REG_SZ 22631 + CurrentBuildNumber REG_SZ 22631 + CurrentType REG_SZ Multiprocessor Free + CurrentVersion REG_SZ 6.3 + DisplayVersion REG_SZ 23H2 + EditionID REG_SZ Professional + EditionSubManufacturer REG_SZ + EditionSubstring REG_SZ + EditionSubVersion REG_SZ + InstallationType REG_SZ Client + ProductName REG_SZ Windows 10 Pro + ReleaseId REG_SZ 2009 + SoftwareType REG_SZ System + PathName REG_SZ C:\Windows + ProductId REG_SZ 00330-80832-91035-AA540 + +End of search: 21 match(es) found. + +'''; + const List wmicCommand = ['wmic', 'os', 'get', 'Caption,OSArchitecture']; + final FakeProcessManager processManager = FakeProcessManager.list( + [ + FakeCommand( + command: wmicCommand, + exception: ProcessException( + wmicCommand[0], + wmicCommand.sublist(1), + ), + ), + const FakeCommand( + command: [ + 'reg', + 'query', + r'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', + '/t', + 'REG_SZ', + ], + stdout: windows10RegQueryOutput, + ), + ], + ); + + final WindowsVersionValidator validator = WindowsVersionValidator( + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: ofdNotRunning(), + versionExtractor: WindowsVersionExtractor( + processManager: processManager, + logger: BufferLogger.test(), + ), + ); + final ValidationResult result = await validator.validate(); + expect(result.type, ValidationType.success); + expect(result.statusInfo, 'Windows 11 or higher, 23H2, 2009'); + }); + + testWithoutContext('Handles reg call failing', () async { + const List wmicCommand = [ + 'wmic', + 'os', + 'get', + 'Caption,OSArchitecture', + ]; + const List regCommand = [ + 'reg', + 'query', + r'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion', + '/t', + 'REG_SZ', + ]; + final FakeProcessManager processManager = FakeProcessManager.list( + [ + const FakeCommand( + command: wmicCommand, + stdout: r''' +Caption OSArchitecture + +Microsoft Windows 11 Pro 64-bit +''' + ), + FakeCommand( + command: regCommand, + exception: ProcessException( + regCommand[0], + regCommand.sublist(1), + ) + ), + ], + ); + + final WindowsVersionValidator validator = WindowsVersionValidator( + operatingSystemUtils: FakeValidOperatingSystemUtils(), + processLister: ofdNotRunning(), + versionExtractor: WindowsVersionExtractor( + processManager: processManager, + logger: BufferLogger.test(), + ), + ); + final ValidationResult result = await validator.validate(); + expect(result.type, ValidationType.success); + expect(result.statusInfo, 'Windows 11 or higher'); + }); } diff --git a/packages/flutter_tools/test/host_cross_arch.shard/macos_content_validation_test.dart b/packages/flutter_tools/test/host_cross_arch.shard/macos_content_validation_test.dart index 2e6af4156e104..d1319d2860e12 100644 --- a/packages/flutter_tools/test/host_cross_arch.shard/macos_content_validation_test.dart +++ b/packages/flutter_tools/test/host_cross_arch.shard/macos_content_validation_test.dart @@ -225,7 +225,19 @@ void main() { expect(outputFlutterFramework.childDirectory('Headers'), isNot(exists)); expect(outputFlutterFramework.childLink('Modules'), isNot(exists)); expect(outputFlutterFramework.childDirectory('Modules'), isNot(exists)); - expect(outputFlutterFramework.childFile('PrivacyInfo.xcprivacy'), exists); + + // PrivacyInfo.xcprivacy was first added to the top-level path, but + // the correct location is Versions/A/Resources/PrivacyInfo.xcprivacy. + // TODO(jmagman): Switch expectation to only check Resources/ once the new path rolls. + // https://github.com/flutter/flutter/issues/157016#issuecomment-2420786225 + final File topLevelPrivacy = outputFlutterFramework.childFile('PrivacyInfo.xcprivacy'); + final File resourcesLevelPrivacy = fileSystem.file(fileSystem.path.join( + outputFlutterFramework.path, + 'Resources', + 'PrivacyInfo.xcprivacy', + )); + + expect(topLevelPrivacy.existsSync() || resourcesLevelPrivacy.existsSync(), isTrue); // Build again without cleaning. final ProcessResult secondBuild = processManager.runSync(buildCommand, workingDirectory: workingDirectory); diff --git a/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart b/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart index bac0d2f80c71b..800ae7e0d329f 100644 --- a/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart +++ b/packages/flutter_tools/test/integration.shard/analyze_suggestions_integration_test.dart @@ -63,38 +63,6 @@ void main() { expect(loggerTest.statusText, contains(expected)); }); - - testUsingContext('PubDependenciesProjectValidator success ', () async { - final BufferLogger loggerTest = BufferLogger.test(); - final AnalyzeCommand command = AnalyzeCommand( - artifacts: globals.artifacts!, - fileSystem: fileSystem, - logger: loggerTest, - platform: globals.platform, - terminal: globals.terminal, - processManager: globals.processManager, - allProjectValidators: [ - PubDependenciesProjectValidator(globals.processManager), - ], - suppressAnalytics: true, - ); - final CommandRunner runner = createTestCommandRunner(command); - - await runner.run([ - 'analyze', - '--no-pub', - '--no-current-package', - '--suggestions', - '../../dev/integration_tests/flutter_gallery', - ]); - - const String expected = '\n' - 'β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”\n' - 'β”‚ Pub dependencies β”‚\n' - 'β”‚ [βœ“] Dart dependencies: All pub dependencies are hosted on https://pub.dartlang.org β”‚\n' - 'β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜\n'; - expect(loggerTest.statusText, contains(expected)); - }); }); group('analyze --suggestions --machine command integration', () { diff --git a/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart b/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart index 9f753e6d278dc..301e851e71ea6 100644 --- a/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart +++ b/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart @@ -54,7 +54,7 @@ void main() { final Directory pluginExampleAppDir = pluginAppDir.childDirectory('example'); - final File projectGradleFile = pluginExampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); + final File projectGradleFile = pluginExampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); expect(projectGradleFile, exists); final String projectBuildGradle = projectGradleFile.readAsStringSync(); diff --git a/packages/flutter_tools/test/integration.shard/android_plugin_example_app_build_test.dart b/packages/flutter_tools/test/integration.shard/android_plugin_example_app_build_test.dart index 9412ba8c434c1..755ef2df44970 100644 --- a/packages/flutter_tools/test/integration.shard/android_plugin_example_app_build_test.dart +++ b/packages/flutter_tools/test/integration.shard/android_plugin_example_app_build_test.dart @@ -51,7 +51,7 @@ void main() { final Directory exampleAppDir = tempDir.childDirectory(testName).childDirectory('example'); - final File buildGradleFile = exampleAppDir.childDirectory('android').childFile('build.gradle'); + final File buildGradleFile = exampleAppDir.childDirectory('android').childFile('build.gradle.kts'); expect(buildGradleFile, exists); final String buildGradle = buildGradleFile.readAsStringSync(); diff --git a/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart b/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart index 93e3cfd7445a2..21784f1b5ee13 100644 --- a/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart +++ b/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart @@ -53,7 +53,7 @@ void main() { final Directory pluginExampleAppDir = pluginAppDir.childDirectory('example'); - final File projectGradleFile = pluginExampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle'); + final File projectGradleFile = pluginExampleAppDir.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); expect(projectGradleFile, exists); final String projectBuildGradle = projectGradleFile.readAsStringSync(); diff --git a/packages/flutter_tools/test/integration.shard/isolated/native_assets_agp_version_test.dart b/packages/flutter_tools/test/integration.shard/isolated/native_assets_agp_version_test.dart index 7c183d1d62611..25f4e2331e66d 100644 --- a/packages/flutter_tools/test/integration.shard/isolated/native_assets_agp_version_test.dart +++ b/packages/flutter_tools/test/integration.shard/isolated/native_assets_agp_version_test.dart @@ -54,8 +54,15 @@ for (final String agpVersion in agpVersions) { final Directory packageDirectory = await createTestProject(packageName, tempDirectory); final Directory exampleDirectory = packageDirectory.childDirectory('example'); - final File buildGradleFile = exampleDirectory.childDirectory('android').childFile('build.gradle'); - final File appBuildGradleFile = exampleDirectory.childDirectory('android').childDirectory('app').childFile('build.gradle'); + File buildGradleFile = exampleDirectory.childDirectory('android').childFile('build.gradle'); + if (!buildGradleFile.existsSync()) { + buildGradleFile = exampleDirectory.childDirectory('android').childFile('build.gradle.kts'); + } + + File appBuildGradleFile = exampleDirectory.childDirectory('android').childDirectory('app').childFile('build.gradle'); + if (!appBuildGradleFile.existsSync()) { + appBuildGradleFile = exampleDirectory.childDirectory('android').childDirectory('app').childFile('build.gradle.kts'); + } expect(buildGradleFile, exists); expect(appBuildGradleFile, exists); diff --git a/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart b/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart index b0d0e28615da3..3776e7447912d 100644 --- a/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart +++ b/packages/flutter_tools/test/integration.shard/swift_package_manager_test.dart @@ -476,6 +476,102 @@ void main() { } }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + test('Build ios-framework with non module app uses CocoaPods', () async { + final Directory workingDirectory = fileSystem.systemTempDirectory + .createTempSync('swift_package_manager_build_framework_non_module.'); + final String workingDirectoryPath = workingDirectory.path; + try { + // Create and build a regular app and framework using the CocoaPods version of + // integration_test even though Swift Package Manager is enabled. + await SwiftPackageManagerUtils.enableSwiftPackageManager(flutterBin, workingDirectoryPath); + + final String appDirectoryPath = await SwiftPackageManagerUtils.createApp( + flutterBin, + workingDirectoryPath, + iosLanguage: 'swift', + platform: 'ios', + usesSwiftPackageManager: true, + options: [], + ); + final SwiftPackageManagerPlugin integrationTestPlugin = SwiftPackageManagerUtils.integrationTestPlugin('ios'); + SwiftPackageManagerUtils.addDependency(appDirectoryPath: appDirectoryPath, plugin: integrationTestPlugin); + + await SwiftPackageManagerUtils.cleanApp(flutterBin, appDirectoryPath); + + await SwiftPackageManagerUtils.buildApp( + flutterBin, + appDirectoryPath, + options: [ + 'ios-framework', + '--xcframework', + '--no-debug', + '--no-profile', + '-v', + ], + expectedLines: [ + 'Swift Package Manager does not yet support this command. CocoaPods will be used instead.', + ], + unexpectedLines: [ + 'Adding Swift Package Manager integration...', + ] + ); + + expect( + fileSystem + .directory(appDirectoryPath) + .childDirectory('.ios') + .existsSync(), + isFalse, + ); + + // TODO(loic-sharma): A Swift package manifest should not be generated. + // https://github.com/flutter/flutter/issues/146957 + // expect( + // fileSystem + // .directory(appDirectoryPath) + // .childDirectory('ios') + // .childDirectory('Flutter') + // .childDirectory('ephemeral') + // .childDirectory('Packages') + // .childDirectory('FlutterGeneratedPluginSwiftPackage') + // .childFile('Package.swift'), + // isFalse, + // ); + + expect( + fileSystem + .directory(appDirectoryPath) + .childDirectory('build') + .childDirectory('ios') + .childDirectory('framework') + .childDirectory('Release') + .childDirectory('${integrationTestPlugin.pluginName}.xcframework') + .existsSync(), + isTrue, + ); + + final File flutterPluginsDependenciesFile = fileSystem + .directory(appDirectoryPath) + .childFile('.flutter-plugins-dependencies'); + + expect(flutterPluginsDependenciesFile.existsSync(), isTrue); + expect( + flutterPluginsDependenciesFile.readAsStringSync(), + isNot(contains('"swift_package_manager_enabled":true')), + ); + expect( + flutterPluginsDependenciesFile.readAsStringSync(), + contains('"swift_package_manager_enabled":false'), + ); + } finally { + await SwiftPackageManagerUtils.disableSwiftPackageManager(flutterBin, workingDirectoryPath); + ErrorHandlingFileSystem.deleteIfExists( + workingDirectory, + recursive: true, + ); + } + }, skip: !platform.isMacOS); // [intended] Swift Package Manager only works on macos. + test("Generated Swift package uses iOS's project minimum deployment", () async { final Directory workingDirectory = fileSystem.systemTempDirectory .createTempSync('swift_package_manager_minimum_deployment_ios.'); diff --git a/packages/flutter_tools/test/src/android_common.dart b/packages/flutter_tools/test/src/android_common.dart index 5f0832b3ce56e..ab2f82e90077c 100644 --- a/packages/flutter_tools/test/src/android_common.dart +++ b/packages/flutter_tools/test/src/android_common.dart @@ -160,7 +160,7 @@ Future buildFlutterApkWithSpecifiedDependencyVersions({ if (versions.compileSdkVersion != null) { final File appGradleBuild = File(fileSystem.path.join( - app.path, 'android', 'app', 'build.gradle')); + app.path, 'android', 'app', 'build.gradle.kts')); final String appBuildContent = appGradleBuild.readAsStringSync() .replaceFirst(flutterCompileSdkString, versions.compileSdkVersion!); appGradleBuild.writeAsStringSync(appBuildContent); diff --git a/packages/flutter_web_plugins/pubspec.yaml b/packages/flutter_web_plugins/pubspec.yaml index 715018d548758..75d5f6b837e9e 100644 --- a/packages/flutter_web_plugins/pubspec.yaml +++ b/packages/flutter_web_plugins/pubspec.yaml @@ -23,8 +23,8 @@ dev_dependencies: boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path: 1.9.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +36,4 @@ dev_dependencies: test_api: 0.7.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 14.3.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: c96b +# PUBSPEC CHECKSUM: f56d diff --git a/packages/integration_test/example/pubspec.yaml b/packages/integration_test/example/pubspec.yaml index ce633eae0e013..273940f42486a 100644 --- a/packages/integration_test/example/pubspec.yaml +++ b/packages/integration_test/example/pubspec.yaml @@ -51,8 +51,8 @@ dev_dependencies: http_parser: 4.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.7.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -88,4 +88,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: de7c +# PUBSPEC CHECKSUM: c57e diff --git a/packages/integration_test/pubspec.yaml b/packages/integration_test/pubspec.yaml index 22180b56cadf6..321360aa398c0 100644 --- a/packages/integration_test/pubspec.yaml +++ b/packages/integration_test/pubspec.yaml @@ -22,8 +22,8 @@ dependencies: collection: 1.19.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 7.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker: 10.0.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - leak_tracker_flutter_testing: 3.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker: 10.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + leak_tracker_flutter_testing: 3.0.9 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" leak_tracker_testing: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.16+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,4 +47,4 @@ flutter: ios: pluginClass: IntegrationTestPlugin -# PUBSPEC CHECKSUM: 53a3 +# PUBSPEC CHECKSUM: 00a5