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.
|
/// 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 {
|
|||||||
|
|
||||||
/// 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
|
@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.
|
/// A widget that builds its child.
|
||||||
|
@ -26,7 +26,9 @@ class ModalBarrier extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new Semantics(
|
return new ExcludeSemantics(
|
||||||
|
excluding: !dismissible,
|
||||||
|
child: new Semantics(
|
||||||
container: true,
|
container: true,
|
||||||
child: new GestureDetector(
|
child: new GestureDetector(
|
||||||
onTapDown: (TapDownDetails details) {
|
onTapDown: (TapDownDetails details) {
|
||||||
@ -43,6 +45,7 @@ class ModalBarrier extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
|||||||
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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user