diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index acbcebe194..7a0627305b 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -3166,6 +3166,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { RenderBox child, bool container = false, bool explicitChildNodes, + bool excludeSemantics = false, bool enabled, bool checked, bool toggled, @@ -3210,6 +3211,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { }) : assert(container != null), _container = container, _explicitChildNodes = explicitChildNodes, + _excludeSemantics = excludeSemantics, _enabled = enabled, _checked = checked, _toggled = toggled, @@ -3291,6 +3293,22 @@ class RenderSemanticsAnnotations extends RenderProxyBox { markNeedsSemanticsUpdate(); } + /// Whether decendants of this [RenderObject] should have their semantic + /// information ignored. + /// + /// When this flag is set to true, all child semantics nodes are ignored. + /// This can be used as a convenience for cases where a child is wrapped in + /// an [ExcludeSemantics] widget and then another [Semantics] widget. + bool get excludeSemantics => _excludeSemantics; + bool _excludeSemantics; + set excludeSemantics(bool value) { + assert(value != null); + if (_excludeSemantics == value) + return; + _excludeSemantics = value; + markNeedsSemanticsUpdate(); + } + /// If non-null, sets the [SemanticsNode.hasCheckedState] semantic to true and /// the [SemanticsNode.isChecked] semantic to the given value. bool get checked => _checked; @@ -3900,6 +3918,14 @@ class RenderSemanticsAnnotations extends RenderProxyBox { markNeedsSemanticsUpdate(); } + @override + void visitChildrenForSemantics(RenderObjectVisitor visitor) { + if (excludeSemantics) + return; + super.visitChildrenForSemantics(visitor); + } + + @override void describeSemanticsConfiguration(SemanticsConfiguration config) { super.describeSemanticsConfiguration(config); diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index c9c5f065d7..34c3c54667 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -5077,6 +5077,7 @@ class Semantics extends SingleChildRenderObjectWidget { Widget child, bool container = false, bool explicitChildNodes = false, + bool excludeSemantics = false, bool enabled, bool checked, bool selected, @@ -5122,6 +5123,7 @@ class Semantics extends SingleChildRenderObjectWidget { child: child, container: container, explicitChildNodes: explicitChildNodes, + excludeSemantics: excludeSemantics, properties: new SemanticsProperties( enabled: enabled, checked: checked, @@ -5174,6 +5176,7 @@ class Semantics extends SingleChildRenderObjectWidget { Widget child, this.container = false, this.explicitChildNodes = false, + this.excludeSemantics = false, @required this.properties, }) : assert(container != null), assert(properties != null), @@ -5210,11 +5213,21 @@ class Semantics extends SingleChildRenderObjectWidget { /// to create semantic boundaries that are either writable or not for children. final bool explicitChildNodes; + /// Whether to replace all child semantics with this node. + /// + /// Defaults to false. + /// + /// When this flag is set to true, all child semantics nodes are ignored. + /// This can be used as a convenience for cases where a child is wrapped in + /// an [ExcludeSemantics] widget and then another [Semantics] widget. + final bool excludeSemantics; + @override RenderSemanticsAnnotations createRenderObject(BuildContext context) { return new RenderSemanticsAnnotations( container: container, explicitChildNodes: explicitChildNodes, + excludeSemantics: excludeSemantics, enabled: properties.enabled, checked: properties.checked, toggled: properties.toggled, @@ -5275,6 +5288,7 @@ class Semantics extends SingleChildRenderObjectWidget { renderObject ..container = container ..explicitChildNodes = explicitChildNodes + ..excludeSemantics = excludeSemantics ..scopesRoute = properties.scopesRoute ..enabled = properties.enabled ..checked = properties.checked diff --git a/packages/flutter/test/widgets/semantics_test.dart b/packages/flutter/test/widgets/semantics_test.dart index f07e849a22..c63da286a9 100644 --- a/packages/flutter/test/widgets/semantics_test.dart +++ b/packages/flutter/test/widgets/semantics_test.dart @@ -1020,6 +1020,31 @@ void main() { handle.dispose(); semantics.dispose(); }); + + testWidgets('Semantics excludeSemantics ignores children', (WidgetTester tester) async { + final SemanticsTester semantics = new SemanticsTester(tester); + await tester.pumpWidget(new Semantics( + label: 'label', + excludeSemantics: true, + textDirection: TextDirection.ltr, + child: new Semantics( + label: 'other label', + textDirection: TextDirection.ltr, + ), + )); + + expect(semantics, hasSemantics( + new TestSemantics( + children: [ + new TestSemantics( + label: 'label', + textDirection: TextDirection.ltr, + ), + ], + ), ignoreId: true, ignoreRect: true, ignoreTransform: true) + ); + semantics.dispose(); + }); } class CustomSortKey extends OrdinalSortKey {