Reland: Add framework-side hitTestBehavior support to Semantics (#178817) (#179352)

This is a reland of #178817, which was reverted in #179100 due to test
failures.

The original PR introduced `hitTestBehavior` to the semantics framework
but incorrectly applied `opaque` behavior to `ModalRoute`, which blocked
platform views from receiving pointer events.

Instead of making the entire modal opaque, we:
1. Keep `ModalRoute` without explicit `hitTestBehavior` (defaults to
`defer`)
2. Make only the dialog/sheet content opaque (blocks clicks to barrier)
3. Platform views remain clickable because they're outside the opaque
content boundary

Fixes #149001
Original PR: #177570
Revert: #178744
This commit is contained in:
zhongliugo 2025-12-10 14:31:02 -08:00 committed by GitHub
parent e25d71b086
commit 2a950efec4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 330 additions and 28 deletions

View File

@ -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<T> extends PopupRoute<T> {
),
);
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;
}

View File

@ -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<T> extends RawDialogRoute<T> {
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,

View File

@ -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!;
}

View File

@ -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!;
}

View File

@ -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<T extends DarwinPlatformViewController>
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;
}
}

View File

@ -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<String>(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<String>? controlsNodes = _controlsNodes;
SemanticsValidationResult validationResult = _validationResult;
ui.SemanticsHitTestBehavior hitTestBehavior = _hitTestBehavior;
SemanticsInputType inputType = _inputType;
final Locale? locale = _locale;
final customSemanticsActionIds = <int>{};
@ -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;
}
}

View File

@ -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<String>? 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,
});

View File

@ -893,11 +893,18 @@ void main() {
TestSemantics(
children: <TestSemantics>[
TestSemantics(
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
children: <TestSemantics>[
TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr),
TestSemantics(
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[
SemanticsFlag.scopesRoute,
SemanticsFlag.namesRoute,
],
children: <TestSemantics>[
TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr),
],
),
],
),
],
@ -1069,14 +1076,24 @@ void main() {
TestSemantics(
children: <TestSemantics>[
TestSemantics(
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[
SemanticsFlag.scopesRoute,
SemanticsFlag.namesRoute,
],
children: <TestSemantics>[
TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
children: <TestSemantics>[
TestSemantics(
label: 'BottomSheet',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
@ -1145,14 +1162,24 @@ void main() {
TestSemantics(
children: <TestSemantics>[
TestSemantics(
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[
SemanticsFlag.scopesRoute,
SemanticsFlag.namesRoute,
],
children: <TestSemantics>[
TestSemantics(label: 'BottomSheet', textDirection: TextDirection.ltr),
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
children: <TestSemantics>[
TestSemantics(
label: 'BottomSheet',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
@ -1217,17 +1244,24 @@ void main() {
TestSemantics(
children: <TestSemantics>[
TestSemantics(
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Dismiss',
label: 'Dialog',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[
SemanticsFlag.scopesRoute,
SemanticsFlag.namesRoute,
],
children: <TestSemantics>[
TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
actions: <SemanticsAction>[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<void>(
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<Semantics> allSemantics = tester
.widgetList<Semantics>(
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 {

View File

@ -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<void>(
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<Semantics>(
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<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Test Dialog'),
content: const SizedBox(width: 200, height: 100),
actions: <Widget>[
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<Semantics>(
find.ancestor(of: find.byType(Dialog), matching: find.byType(Semantics)).first,
);
expect(routeSemantics.properties.hitTestBehavior, SemanticsHitTestBehavior.opaque);
semantics.dispose();
});
}
@pragma('vm:entry-point')

View File

@ -1460,6 +1460,7 @@ void main() {
role: SemanticsRole.menu,
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
label: 'Popup menu',
textDirection: TextDirection.ltr,
children: <TestSemantics>[
TestSemantics(
children: <TestSemantics>[

View File

@ -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: <Factory<OneSequenceGestureRecognizer>>{},
);
final config = SemanticsConfiguration();
renderBox.describeSemanticsConfiguration(config);
expect(config.hitTestBehavior, ui.SemanticsHitTestBehavior.transparent);
expect(config.isSemanticBoundary, true);
expect(config.platformViewId, 0);
});
}
ui.PointerData _pointerData(

View File

@ -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(

View File

@ -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,
);