diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart index 00fa0ec6db4..77c042db4c0 100644 --- a/packages/flutter/lib/src/material/bottom_sheet.dart +++ b/packages/flutter/lib/src/material/bottom_sheet.dart @@ -6,6 +6,7 @@ library; import 'dart:math' as math; +import 'dart:ui' show SemanticsHitTestBehavior; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; @@ -1117,10 +1118,13 @@ class ModalBottomSheetRoute extends PopupRoute { ), ); - final Widget bottomSheet = useSafeArea + Widget bottomSheet = useSafeArea ? SafeArea(bottom: false, child: content) : MediaQuery.removePadding(context: context, removeTop: true, child: content); + // Prevent clicks inside the bottom sheet from passing through to the barrier + bottomSheet = Semantics(hitTestBehavior: SemanticsHitTestBehavior.opaque, child: bottomSheet); + return capturedThemes?.wrap(bottomSheet) ?? bottomSheet; } diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 78d1369e3a5..e4c6d2e8017 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -10,7 +10,7 @@ /// @docImport 'text_button.dart'; library; -import 'dart:ui' show SemanticsRole, clampDouble, lerpDouble; +import 'dart:ui' show SemanticsHitTestBehavior, SemanticsRole, clampDouble, lerpDouble; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; @@ -1676,6 +1676,8 @@ class DialogRoute extends RawDialogRoute { if (useSafeArea) { dialog = SafeArea(child: dialog); } + // Prevent clicks inside the dialog from passing through to the barrier + dialog = Semantics(hitTestBehavior: SemanticsHitTestBehavior.opaque, child: dialog); return dialog; }, barrierLabel: barrierLabel ?? MaterialLocalizations.of(context).modalBarrierDismissLabel, diff --git a/packages/flutter/lib/src/rendering/custom_paint.dart b/packages/flutter/lib/src/rendering/custom_paint.dart index 298a34532ba..d10dfe738a3 100644 --- a/packages/flutter/lib/src/rendering/custom_paint.dart +++ b/packages/flutter/lib/src/rendering/custom_paint.dart @@ -1043,6 +1043,9 @@ class RenderCustomPaint extends RenderProxyBox { if (config.validationResult != properties.validationResult) { config.validationResult = properties.validationResult; } + if (properties.hitTestBehavior != null) { + config.hitTestBehavior = properties.hitTestBehavior!; + } if (properties.inputType != null) { config.inputType = properties.inputType!; } diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index b5c39afa12f..15da70fd1af 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -4938,6 +4938,10 @@ mixin SemanticsAnnotationsMixin on RenderObject { config.validationResult = _properties.validationResult; } + if (_properties.hitTestBehavior != null) { + config.hitTestBehavior = _properties.hitTestBehavior!; + } + if (_properties.inputType != null) { config.inputType = _properties.inputType!; } diff --git a/packages/flutter/lib/src/rendering/platform_view.dart b/packages/flutter/lib/src/rendering/platform_view.dart index ca833e3f2a3..e2830e97f0c 100644 --- a/packages/flutter/lib/src/rendering/platform_view.dart +++ b/packages/flutter/lib/src/rendering/platform_view.dart @@ -5,6 +5,8 @@ /// @docImport 'package:flutter/widgets.dart'; library; +import 'dart:ui' as ui show SemanticsHitTestBehavior; + import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/scheduler.dart'; @@ -267,6 +269,9 @@ class RenderAndroidView extends PlatformViewRenderBox { if (_viewController.isCreated) { config.platformViewId = _viewController.viewId; + // Platform views should allow pointer events to pass through to the + // underlying platform view content. + config.hitTestBehavior = ui.SemanticsHitTestBehavior.transparent; } } } @@ -372,6 +377,9 @@ abstract class RenderDarwinPlatformView super.describeSemanticsConfiguration(config); config.isSemanticBoundary = true; config.platformViewId = _viewController.id; + // Platform views should allow pointer events to pass through to the + // underlying platform view content. + config.hitTestBehavior = ui.SemanticsHitTestBehavior.transparent; } @override @@ -737,6 +745,9 @@ class PlatformViewRenderBox extends RenderBox with _PlatformViewGestureMixin { super.describeSemanticsConfiguration(config); config.isSemanticBoundary = true; config.platformViewId = _controller.viewId; + // Platform views should allow pointer events to pass through to the + // underlying platform view content. + config.hitTestBehavior = ui.SemanticsHitTestBehavior.transparent; } } diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart index 8bf5f290c9a..86f44a09247 100644 --- a/packages/flutter/lib/src/semantics/semantics.dart +++ b/packages/flutter/lib/src/semantics/semantics.dart @@ -28,6 +28,7 @@ import 'dart:ui' StringAttribute, TextDirection, Tristate; +import 'dart:ui' as ui show SemanticsHitTestBehavior; import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; @@ -1020,6 +1021,7 @@ class SemanticsData with Diagnosticable { required this.role, required this.controlsNodes, required this.validationResult, + required this.hitTestBehavior, required this.inputType, required this.locale, this.tags, @@ -1288,6 +1290,9 @@ class SemanticsData with Diagnosticable { /// {@macro flutter.semantics.SemanticsProperties.validationResult} final SemanticsValidationResult validationResult; + /// {@macro flutter.semantics.SemanticsProperties.hitTestBehavior} + final ui.SemanticsHitTestBehavior hitTestBehavior; + /// {@macro flutter.semantics.SemanticsNode.inputType} final SemanticsInputType inputType; @@ -1415,6 +1420,7 @@ class SemanticsData with Diagnosticable { other.role == role && other.validationResult == validationResult && other.inputType == inputType && + other.hitTestBehavior == hitTestBehavior && _sortedListsEqual(other.customSemanticsActionIds, customSemanticsActionIds) && setEquals(controlsNodes, other.controlsNodes); } @@ -1451,8 +1457,7 @@ class SemanticsData with Diagnosticable { validationResult, controlsNodes == null ? null : Object.hashAll(controlsNodes!), inputType, - traversalParentIdentifier, - traversalChildIdentifier, + hitTestBehavior, ), ); @@ -1611,6 +1616,7 @@ class SemanticsProperties extends DiagnosticableTree { this.controlsNodes, this.inputType, this.validationResult = SemanticsValidationResult.none, + this.hitTestBehavior, this.onTap, this.onLongPress, this.onScrollLeft, @@ -2555,6 +2561,13 @@ class SemanticsProperties extends DiagnosticableTree { /// {@endtemplate} final SemanticsValidationResult validationResult; + /// {@template flutter.semantics.SemanticsProperties.hitTestBehavior} + /// Describes how the semantic node should behave during hit testing. + /// + /// See [ui.SemanticsHitTestBehavior] for more details. + /// {@endtemplate} + final ui.SemanticsHitTestBehavior? hitTestBehavior; + /// {@template flutter.semantics.SemanticsProperties.inputType} /// The input type for of a editable widget. /// @@ -3235,7 +3248,8 @@ class SemanticsNode with DiagnosticableTreeMixin { _headingLevel != config._headingLevel || _linkUrl != config._linkUrl || _role != config.role || - _validationResult != config.validationResult; + _validationResult != config.validationResult || + _hitTestBehavior != config.hitTestBehavior; } // TAGS, LABELS, ACTIONS @@ -3529,6 +3543,10 @@ class SemanticsNode with DiagnosticableTreeMixin { SemanticsValidationResult get validationResult => _validationResult; SemanticsValidationResult _validationResult = _kEmptyConfig.validationResult; + /// {@macro flutter.semantics.SemanticsProperties.hitTestBehavior} + ui.SemanticsHitTestBehavior get hitTestBehavior => _hitTestBehavior; + ui.SemanticsHitTestBehavior _hitTestBehavior = ui.SemanticsHitTestBehavior.defer; + /// {@template flutter.semantics.SemanticsNode.inputType} /// The input type for of a editable node. /// @@ -3609,6 +3627,7 @@ class SemanticsNode with DiagnosticableTreeMixin { _role = config._role; _controlsNodes = config._controlsNodes; _validationResult = config._validationResult; + _hitTestBehavior = config._hitTestBehavior; _inputType = config._inputType; _locale = config.locale; @@ -3663,6 +3682,7 @@ class SemanticsNode with DiagnosticableTreeMixin { SemanticsRole role = _role; Set? controlsNodes = _controlsNodes; SemanticsValidationResult validationResult = _validationResult; + ui.SemanticsHitTestBehavior hitTestBehavior = _hitTestBehavior; SemanticsInputType inputType = _inputType; final Locale? locale = _locale; final customSemanticsActionIds = {}; @@ -3727,6 +3747,9 @@ class SemanticsNode with DiagnosticableTreeMixin { if (inputType == SemanticsInputType.none) { inputType = node._inputType; } + if (hitTestBehavior == ui.SemanticsHitTestBehavior.defer) { + hitTestBehavior = node._hitTestBehavior; + } if (tooltip == '') { tooltip = node._tooltip; } @@ -3818,6 +3841,7 @@ class SemanticsNode with DiagnosticableTreeMixin { role: role, controlsNodes: controlsNodes, validationResult: validationResult, + hitTestBehavior: hitTestBehavior, inputType: inputType, locale: locale, ); @@ -3989,6 +4013,7 @@ class SemanticsNode with DiagnosticableTreeMixin { role: data.role, controlsNodes: data.controlsNodes?.toList(), validationResult: data.validationResult, + hitTestBehavior: data.hitTestBehavior, inputType: data.inputType, locale: data.locale, ); @@ -6412,6 +6437,14 @@ class SemanticsConfiguration { _hasBeenAnnotated = true; } + /// {@macro flutter.semantics.SemanticsProperties.hitTestBehavior} + ui.SemanticsHitTestBehavior get hitTestBehavior => _hitTestBehavior; + ui.SemanticsHitTestBehavior _hitTestBehavior = ui.SemanticsHitTestBehavior.defer; + set hitTestBehavior(ui.SemanticsHitTestBehavior value) { + _hitTestBehavior = value; + _hasBeenAnnotated = true; + } + /// {@macro flutter.semantics.SemanticsProperties.inputType} SemanticsInputType get inputType => _inputType; SemanticsInputType _inputType = SemanticsInputType.none; @@ -6523,6 +6556,10 @@ class SemanticsConfiguration { if (_hasExplicitRole && other._hasExplicitRole) { return false; } + if (_hitTestBehavior != ui.SemanticsHitTestBehavior.defer || + other._hitTestBehavior != ui.SemanticsHitTestBehavior.defer) { + return false; + } return true; } @@ -6633,6 +6670,11 @@ class SemanticsConfiguration { child._accessiblityFocusBlockType, ); + if (_hitTestBehavior == ui.SemanticsHitTestBehavior.defer && + child._hitTestBehavior != ui.SemanticsHitTestBehavior.defer) { + _hitTestBehavior = child._hitTestBehavior; + } + _hasBeenAnnotated = hasBeenAnnotated || child.hasBeenAnnotated; } @@ -6678,7 +6720,8 @@ class SemanticsConfiguration { .._role = _role .._controlsNodes = _controlsNodes .._validationResult = _validationResult - .._inputType = _inputType; + .._inputType = _inputType + .._hitTestBehavior = _hitTestBehavior; } } diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 779882d7495..ed1c9096461 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -9,7 +9,9 @@ library; import 'dart:math' as math; -import 'dart:ui' as ui show Image, ImageFilter, SemanticsInputType, TextHeightBehavior; +import 'dart:ui' + as ui + show Image, ImageFilter, SemanticsHitTestBehavior, SemanticsInputType, TextHeightBehavior; import 'package:flutter/animation.dart'; import 'package:flutter/foundation.dart'; @@ -4047,6 +4049,7 @@ sealed class _SemanticsBase extends SingleChildRenderObjectWidget { required SemanticsRole? role, required Set? controlsNodes, required SemanticsValidationResult validationResult, + required ui.SemanticsHitTestBehavior? hitTestBehavior, required ui.SemanticsInputType? inputType, required Locale? localeForSubtree, }) : this.fromProperties( @@ -4132,6 +4135,7 @@ sealed class _SemanticsBase extends SingleChildRenderObjectWidget { role: role, controlsNodes: controlsNodes, validationResult: validationResult, + hitTestBehavior: hitTestBehavior, inputType: inputType, ), ); @@ -4377,6 +4381,7 @@ class SliverSemantics extends _SemanticsBase { super.role, super.controlsNodes, super.validationResult = SemanticsValidationResult.none, + super.hitTestBehavior, super.inputType, super.localeForSubtree, }) : super(child: sliver); @@ -7959,6 +7964,7 @@ class Semantics extends _SemanticsBase { super.role, super.controlsNodes, super.validationResult = SemanticsValidationResult.none, + super.hitTestBehavior, super.inputType, super.localeForSubtree, }); diff --git a/packages/flutter/test/material/bottom_sheet_test.dart b/packages/flutter/test/material/bottom_sheet_test.dart index 9753cca8b98..6c2281dc29a 100644 --- a/packages/flutter/test/material/bottom_sheet_test.dart +++ b/packages/flutter/test/material/bottom_sheet_test.dart @@ -893,11 +893,18 @@ void main() { TestSemantics( children: [ TestSemantics( - label: 'Dialog', - textDirection: TextDirection.ltr, - flags: [SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute], children: [ - TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), + TestSemantics( + label: 'Dialog', + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.scopesRoute, + SemanticsFlag.namesRoute, + ], + children: [ + TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), + ], + ), ], ), ], @@ -1069,14 +1076,24 @@ void main() { TestSemantics( children: [ TestSemantics( - label: 'Dialog', - textDirection: TextDirection.ltr, - flags: [SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute], children: [ TestSemantics( - flags: [SemanticsFlag.hasImplicitScrolling], + label: 'Dialog', + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.scopesRoute, + SemanticsFlag.namesRoute, + ], children: [ - TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), + TestSemantics( + flags: [SemanticsFlag.hasImplicitScrolling], + children: [ + TestSemantics( + label: 'BottomSheet', + textDirection: TextDirection.ltr, + ), + ], + ), ], ), ], @@ -1145,14 +1162,24 @@ void main() { TestSemantics( children: [ TestSemantics( - label: 'Dialog', - textDirection: TextDirection.ltr, - flags: [SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute], children: [ TestSemantics( - flags: [SemanticsFlag.hasImplicitScrolling], + label: 'Dialog', + textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.scopesRoute, + SemanticsFlag.namesRoute, + ], children: [ - TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), + TestSemantics( + flags: [SemanticsFlag.hasImplicitScrolling], + children: [ + TestSemantics( + label: 'BottomSheet', + textDirection: TextDirection.ltr, + ), + ], + ), ], ), ], @@ -1217,17 +1244,24 @@ void main() { TestSemantics( children: [ TestSemantics( - label: 'Dialog', - textDirection: TextDirection.ltr, - flags: [SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute], children: [ TestSemantics( - flags: [SemanticsFlag.isButton], - actions: [SemanticsAction.tap], - label: 'Dismiss', + label: 'Dialog', textDirection: TextDirection.ltr, + flags: [ + SemanticsFlag.scopesRoute, + SemanticsFlag.namesRoute, + ], + children: [ + TestSemantics( + flags: [SemanticsFlag.isButton], + actions: [SemanticsAction.tap], + label: 'Dismiss', + textDirection: TextDirection.ltr, + ), + TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), + ], ), - TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr), ], ), ], @@ -2958,6 +2992,61 @@ void main() { // Test with theme.platform = iOS on different real platforms. await pumpModalBottomSheetWithTheme(TargetPlatform.iOS); }, variant: TargetPlatformVariant.all()); + + testWidgets('Modal bottom sheet has hitTestBehavior.opaque to prevent dismissal on empty areas', ( + WidgetTester tester, + ) async { + final semantics = SemanticsTester(tester); + late BuildContext savedContext; + + await tester.pumpWidget( + MaterialApp( + home: Builder( + builder: (BuildContext context) { + savedContext = context; + return Container(); + }, + ), + ), + ); + + await tester.pump(); + + showModalBottomSheet( + context: savedContext, + builder: (BuildContext context) => Container( + height: 200, + color: Colors.blue, + child: const Center(child: Text('Modal Bottom Sheet')), + ), + ); + + await tester.pumpAndSettle(); + + expect(find.text('Modal Bottom Sheet'), findsOneWidget); + + // Verify the route-level Semantics has opaque hitTestBehavior + // This prevents clicks inside the bottom sheet from passing through to the barrier + final List allSemantics = tester + .widgetList( + find.ancestor(of: find.text('Modal Bottom Sheet'), matching: find.byType(Semantics)), + ) + .toList(); + + final Semantics routeSemantics = allSemantics.firstWhere( + (Semantics s) => s.properties.hitTestBehavior == SemanticsHitTestBehavior.opaque, + ); + + expect(routeSemantics.properties.hitTestBehavior, SemanticsHitTestBehavior.opaque); + + final Semantics widgetSemantics = allSemantics.firstWhere( + (Semantics s) => s.properties.scopesRoute ?? false, + ); + + expect(widgetSemantics.properties.scopesRoute, true); + + semantics.dispose(); + }); } class _TestPage extends StatelessWidget { diff --git a/packages/flutter/test/material/dialog_test.dart b/packages/flutter/test/material/dialog_test.dart index 85581c6c990..76f4a98d121 100644 --- a/packages/flutter/test/material/dialog_test.dart +++ b/packages/flutter/test/material/dialog_test.dart @@ -3311,6 +3311,95 @@ void main() { semantics.dispose(); }, variant: TargetPlatformVariant.all()); }); + + testWidgets('Dialog has hitTestBehavior.opaque to prevent dismissal on empty areas', ( + WidgetTester tester, + ) async { + final semantics = SemanticsTester(tester); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) => + const Dialog(child: SizedBox(width: 200, height: 200)), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ), + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.byType(Dialog), findsOneWidget); + + final Semantics routeSemantics = tester.widget( + find.ancestor(of: find.byType(Dialog), matching: find.byType(Semantics)).first, + ); + + expect(routeSemantics.properties.hitTestBehavior, SemanticsHitTestBehavior.opaque); + + semantics.dispose(); + }); + + testWidgets('AlertDialog has hitTestBehavior.opaque via Dialog', (WidgetTester tester) async { + final semantics = SemanticsTester(tester); + + await tester.pumpWidget( + MaterialApp( + home: Material( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) => AlertDialog( + title: const Text('Test Dialog'), + content: const SizedBox(width: 200, height: 100), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('OK'), + ), + ], + ), + ); + }, + child: const Text('Show Dialog'), + ); + }, + ), + ), + ), + ); + + await tester.tap(find.text('Show Dialog')); + await tester.pumpAndSettle(); + + expect(find.byType(AlertDialog), findsOneWidget); + expect(find.byType(Dialog), findsOneWidget); + + // Find the route-level Semantics with hitTestBehavior.opaque + // (wraps the entire dialog content, above the Dialog widget) + final Semantics routeSemantics = tester.widget( + find.ancestor(of: find.byType(Dialog), matching: find.byType(Semantics)).first, + ); + + expect(routeSemantics.properties.hitTestBehavior, SemanticsHitTestBehavior.opaque); + + semantics.dispose(); + }); } @pragma('vm:entry-point') diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 4b884d503b8..b0917cdb62c 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -1460,6 +1460,7 @@ void main() { role: SemanticsRole.menu, flags: [SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute], label: 'Popup menu', + textDirection: TextDirection.ltr, children: [ TestSemantics( children: [ diff --git a/packages/flutter/test/rendering/platform_view_test.dart b/packages/flutter/test/rendering/platform_view_test.dart index 35780bfabb8..ab28d30b838 100644 --- a/packages/flutter/test/rendering/platform_view_test.dart +++ b/packages/flutter/test/rendering/platform_view_test.dart @@ -557,6 +557,22 @@ void main() { }); }); }); + + test('PlatformViewRenderBox has transparent hitTestBehavior in semantics', () { + final controller = FakePlatformViewController(0); + final renderBox = PlatformViewRenderBox( + controller: controller, + hitTestBehavior: PlatformViewHitTestBehavior.opaque, + gestureRecognizers: >{}, + ); + + final config = SemanticsConfiguration(); + renderBox.describeSemanticsConfiguration(config); + + expect(config.hitTestBehavior, ui.SemanticsHitTestBehavior.transparent); + expect(config.isSemanticBoundary, true); + expect(config.platformViewId, 0); + }); } ui.PointerData _pointerData( diff --git a/packages/flutter/test/semantics/semantics_test.dart b/packages/flutter/test/semantics/semantics_test.dart index 9523b53cced..b34f74baf94 100644 --- a/packages/flutter/test/semantics/semantics_test.dart +++ b/packages/flutter/test/semantics/semantics_test.dart @@ -1035,6 +1035,34 @@ void main() { expect(config.customSemanticsActions[customAction], same(onCustomAction)); }); + test('SemanticsConfiguration.copy() preserves hitTestBehavior', () { + final config = SemanticsConfiguration() + ..isSemanticBoundary = true + ..label = 'test' + ..hitTestBehavior = SemanticsHitTestBehavior.opaque; + + expect(config.hitTestBehavior, SemanticsHitTestBehavior.opaque); + + final SemanticsConfiguration copy = config.copy(); + + expect(copy.hitTestBehavior, SemanticsHitTestBehavior.opaque); + expect(copy.isSemanticBoundary, isTrue); + expect(copy.label, 'test'); + }); + + test('SemanticsConfiguration.copy() preserves all hitTestBehavior values', () { + final deferConfig = SemanticsConfiguration(); + expect(deferConfig.copy().hitTestBehavior, SemanticsHitTestBehavior.defer); + + final opaqueConfig = SemanticsConfiguration() + ..hitTestBehavior = SemanticsHitTestBehavior.opaque; + expect(opaqueConfig.copy().hitTestBehavior, SemanticsHitTestBehavior.opaque); + + final transparentConfig = SemanticsConfiguration() + ..hitTestBehavior = SemanticsHitTestBehavior.transparent; + expect(transparentConfig.copy().hitTestBehavior, SemanticsHitTestBehavior.transparent); + }); + test('SemanticsOwner dispatches memory events', () async { await expectLater( await memoryEvents( diff --git a/packages/flutter_test/test/matchers_test.dart b/packages/flutter_test/test/matchers_test.dart index bc9f931211f..0e6b42b9aa8 100644 --- a/packages/flutter_test/test/matchers_test.dart +++ b/packages/flutter_test/test/matchers_test.dart @@ -753,6 +753,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, ); @@ -1055,6 +1056,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, ); @@ -1157,6 +1159,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, ); @@ -1264,6 +1267,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, ); @@ -1299,6 +1303,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, ); @@ -1433,6 +1438,7 @@ void main() { role: ui.SemanticsRole.none, controlsNodes: null, validationResult: SemanticsValidationResult.none, + hitTestBehavior: ui.SemanticsHitTestBehavior.defer, inputType: ui.SemanticsInputType.none, locale: null, );