Exclude ModalBarrier from Semantics Tree if it is not dismissible (#10395)
This commit is contained in:
parent
d14bb2cdfc
commit
87c5b24e87
@ -2901,12 +2901,41 @@ class RenderMergeSemantics extends RenderProxyBox {
|
||||
|
||||
/// 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
|
||||
/// to it (e.g. text included only for the visual effect).
|
||||
class RenderExcludeSemantics extends RenderProxyBox {
|
||||
/// 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
|
||||
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 {
|
||||
|
||||
/// 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
|
||||
/// reported but that would only be confusing. For example, the
|
||||
/// material library's [Chip] widget hides the avatar since it is
|
||||
/// redundant with the chip label.
|
||||
class ExcludeSemantics extends SingleChildRenderObjectWidget {
|
||||
/// 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();
|
||||
RenderExcludeSemantics createRenderObject(BuildContext context) => new RenderExcludeSemantics(excluding: excluding);
|
||||
|
||||
@override
|
||||
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.
|
||||
|
@ -26,19 +26,22 @@ class ModalBarrier extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return new Semantics(
|
||||
container: true,
|
||||
child: new GestureDetector(
|
||||
onTapDown: (TapDownDetails details) {
|
||||
if (dismissible)
|
||||
Navigator.pop(context);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new ConstrainedBox(
|
||||
constraints: const BoxConstraints.expand(),
|
||||
child: color == null ? null : new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
color: color
|
||||
return new ExcludeSemantics(
|
||||
excluding: !dismissible,
|
||||
child: new Semantics(
|
||||
container: true,
|
||||
child: new GestureDetector(
|
||||
onTapDown: (TapDownDetails details) {
|
||||
if (dismissible)
|
||||
Navigator.pop(context);
|
||||
},
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new ConstrainedBox(
|
||||
constraints: const BoxConstraints.expand(),
|
||||
child: color == null ? null : new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
color: color
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -4,8 +4,11 @@
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
bool tapped;
|
||||
Widget tapTarget;
|
||||
@ -78,6 +81,40 @@ void main() {
|
||||
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing,
|
||||
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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user