Commit 87c5b24e authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Exclude ModalBarrier from Semantics Tree if it is not dismissible (#10395)

parent d14bb2cd
...@@ -2901,12 +2901,41 @@ class RenderMergeSemantics extends RenderProxyBox { ...@@ -2901,12 +2901,41 @@ class RenderMergeSemantics extends RenderProxyBox {
/// Excludes this subtree from the semantic tree. /// Excludes this subtree from the semantic tree.
/// ///
/// When [excluding] is true, this render object (and its subtree) is excluded
/// from the semantic tree.
///
/// Useful e.g. for hiding text that is redundant with other text next /// Useful e.g. for hiding text that is redundant with other text next
/// to it (e.g. text included only for the visual effect). /// to it (e.g. text included only for the visual effect).
class RenderExcludeSemantics extends RenderProxyBox { class RenderExcludeSemantics extends RenderProxyBox {
/// Creates a render object that ignores the semantics of its subtree. /// Creates a render object that ignores the semantics of its subtree.
RenderExcludeSemantics({ RenderBox child }) : super(child); RenderExcludeSemantics({
RenderBox child,
bool excluding: true,
}) : _excluding = excluding, super(child) {
assert(_excluding != null);
}
/// Whether this render object is excluded from the semantic tree.
bool get excluding => _excluding;
bool _excluding;
set excluding(bool value) {
assert(value != null);
if (value == _excluding)
return;
_excluding = value;
markNeedsSemanticsUpdate();
}
@override @override
void visitChildrenForSemantics(RenderObjectVisitor visitor) { } void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (excluding)
return;
super.visitChildrenForSemantics(visitor);
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('excluding: $excluding');
}
} }
...@@ -3424,16 +3424,38 @@ class MergeSemantics extends SingleChildRenderObjectWidget { ...@@ -3424,16 +3424,38 @@ class MergeSemantics extends SingleChildRenderObjectWidget {
/// A widget that drops all the semantics of its descendants. /// A widget that drops all the semantics of its descendants.
/// ///
/// When [excluding] is true, this widget (and its subtree) is excluded from
/// the semantics tree.
///
/// This can be used to hide subwidgets that would otherwise be /// This can be used to hide subwidgets that would otherwise be
/// reported but that would only be confusing. For example, the /// reported but that would only be confusing. For example, the
/// material library's [Chip] widget hides the avatar since it is /// material library's [Chip] widget hides the avatar since it is
/// redundant with the chip label. /// redundant with the chip label.
class ExcludeSemantics extends SingleChildRenderObjectWidget { class ExcludeSemantics extends SingleChildRenderObjectWidget {
/// Creates a widget that drops all the semantics of its descendants. /// Creates a widget that drops all the semantics of its descendants.
const ExcludeSemantics({ Key key, Widget child }) : super(key: key, child: child); const ExcludeSemantics({
Key key,
this.excluding: true,
Widget child,
}) : assert(excluding != null),
super(key: key, child: child);
/// Whether this widget is excluded in the semantics tree.
final bool excluding;
@override
RenderExcludeSemantics createRenderObject(BuildContext context) => new RenderExcludeSemantics(excluding: excluding);
@override @override
RenderExcludeSemantics createRenderObject(BuildContext context) => new RenderExcludeSemantics(); void updateRenderObject(BuildContext context, RenderExcludeSemantics renderObject) {
renderObject.excluding = excluding;
}
@override
void debugFillDescription(List<String> description) {
super.debugFillDescription(description);
description.add('excluding: $excluding');
}
} }
/// A widget that builds its child. /// A widget that builds its child.
......
...@@ -26,19 +26,22 @@ class ModalBarrier extends StatelessWidget { ...@@ -26,19 +26,22 @@ class ModalBarrier extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new Semantics( return new ExcludeSemantics(
container: true, excluding: !dismissible,
child: new GestureDetector( child: new Semantics(
onTapDown: (TapDownDetails details) { container: true,
if (dismissible) child: new GestureDetector(
Navigator.pop(context); onTapDown: (TapDownDetails details) {
}, if (dismissible)
behavior: HitTestBehavior.opaque, Navigator.pop(context);
child: new ConstrainedBox( },
constraints: const BoxConstraints.expand(), behavior: HitTestBehavior.opaque,
child: color == null ? null : new DecoratedBox( child: new ConstrainedBox(
decoration: new BoxDecoration( constraints: const BoxConstraints.expand(),
color: color child: color == null ? null : new DecoratedBox(
decoration: new BoxDecoration(
color: color
)
) )
) )
) )
......
...@@ -4,8 +4,11 @@ ...@@ -4,8 +4,11 @@
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'semantics_tester.dart';
void main() { void main() {
bool tapped; bool tapped;
Widget tapTarget; Widget tapTarget;
...@@ -78,6 +81,40 @@ void main() { ...@@ -78,6 +81,40 @@ void main() {
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing, expect(find.byKey(const ValueKey<String>('barrier')), findsNothing,
reason: 'The route should have been dismissed by tapping the barrier.'); reason: 'The route should have been dismissed by tapping the barrier.');
}); });
testWidgets('Undismissible ModalBarrier hidden in semantic tree', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(const ModalBarrier(dismissible: false));
final TestSemantics expectedSemantics = new TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
});
testWidgets('Dismissible ModalBarrier includes button in semantic tree', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(const ModalBarrier(dismissible: true));
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 1,
rect: TestSemantics.fullScreen,
children: <TestSemantics>[
new TestSemantics(
id: 2,
rect: TestSemantics.fullScreen,
actions: SemanticsAction.tap.index,
),
]
),
]
);
expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
});
} }
class FirstWidget extends StatelessWidget { class FirstWidget extends StatelessWidget {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment