Unverified Commit 5b30b393 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

make sure bottom nav bar always contributes labels (#19934)

parent 3a6228be
...@@ -242,6 +242,7 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -242,6 +242,7 @@ class _BottomNavigationTile extends StatelessWidget {
).evaluate(animation), ).evaluate(animation),
), ),
child: new FadeTransition( child: new FadeTransition(
alwaysIncludeSemantics: true,
opacity: animation, opacity: animation,
child: DefaultTextStyle.merge( child: DefaultTextStyle.merge(
style: const TextStyle( style: const TextStyle(
......
...@@ -792,7 +792,14 @@ class RenderAnimatedOpacity extends RenderProxyBox { ...@@ -792,7 +792,14 @@ class RenderAnimatedOpacity extends RenderProxyBox {
/// Creates a partially transparent render object. /// Creates a partially transparent render object.
/// ///
/// The [opacity] argument must not be null. /// The [opacity] argument must not be null.
RenderAnimatedOpacity({ @required Animation<double> opacity, RenderBox child }) : super(child) { RenderAnimatedOpacity({
@required Animation<double> opacity,
bool alwaysIncludeSemantics = false,
RenderBox child,
}) : assert(opacity != null),
assert(alwaysIncludeSemantics != null),
_alwaysIncludeSemantics = alwaysIncludeSemantics,
super(child) {
this.opacity = opacity; this.opacity = opacity;
} }
...@@ -823,6 +830,18 @@ class RenderAnimatedOpacity extends RenderProxyBox { ...@@ -823,6 +830,18 @@ class RenderAnimatedOpacity extends RenderProxyBox {
_updateOpacity(); _updateOpacity();
} }
/// Whether child semantics are included regardless of the opacity.
///
/// Defaults to false.
bool get alwaysIncludeSemantics => _alwaysIncludeSemantics;
bool _alwaysIncludeSemantics;
set alwaysIncludeSemantics(bool value) {
if (value == _alwaysIncludeSemantics)
return;
_alwaysIncludeSemantics = value;
markNeedsSemanticsUpdate();
}
@override @override
void attach(PipelineOwner owner) { void attach(PipelineOwner owner) {
super.attach(owner); super.attach(owner);
...@@ -866,7 +885,7 @@ class RenderAnimatedOpacity extends RenderProxyBox { ...@@ -866,7 +885,7 @@ class RenderAnimatedOpacity extends RenderProxyBox {
@override @override
void visitChildrenForSemantics(RenderObjectVisitor visitor) { void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (child != null && _alpha != 0) if (child != null && (_alpha != 0 || alwaysIncludeSemantics))
visitor(child); visitor(child);
} }
......
...@@ -342,6 +342,7 @@ class FadeTransition extends SingleChildRenderObjectWidget { ...@@ -342,6 +342,7 @@ class FadeTransition extends SingleChildRenderObjectWidget {
Key key, Key key,
@required this.opacity, @required this.opacity,
Widget child, Widget child,
this.alwaysIncludeSemantics = false,
}) : super(key: key, child: child); }) : super(key: key, child: child);
/// The animation that controls the opacity of the child. /// The animation that controls the opacity of the child.
...@@ -352,17 +353,29 @@ class FadeTransition extends SingleChildRenderObjectWidget { ...@@ -352,17 +353,29 @@ class FadeTransition extends SingleChildRenderObjectWidget {
/// completely transparent. /// completely transparent.
final Animation<double> opacity; final Animation<double> opacity;
/// Whether the semantic information of the children is always included.
///
/// Defaults to false.
///
/// When true, regardless of the opacity settings the child semantic
/// information is exposed as if the widget were fully visible. This is
/// useful in cases where labels may be hidden during animations that
/// would otherwise contribute relevant semantics.
final bool alwaysIncludeSemantics;
@override @override
RenderAnimatedOpacity createRenderObject(BuildContext context) { RenderAnimatedOpacity createRenderObject(BuildContext context) {
return new RenderAnimatedOpacity( return new RenderAnimatedOpacity(
opacity: opacity, opacity: opacity,
alwaysIncludeSemantics: alwaysIncludeSemantics,
); );
} }
@override @override
void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) { void updateRenderObject(BuildContext context, RenderAnimatedOpacity renderObject) {
renderObject renderObject
..opacity = opacity; ..opacity = opacity
..alwaysIncludeSemantics = alwaysIncludeSemantics;
} }
@override @override
......
...@@ -539,7 +539,7 @@ void main() { ...@@ -539,7 +539,7 @@ void main() {
expect(find.byKey(stroked), findsOneWidget); expect(find.byKey(stroked), findsOneWidget);
}); });
testWidgets('BottomNavigationBar semantics', (WidgetTester tester) async { testWidgets('BottomNavigationBar.fixed semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -567,13 +567,10 @@ void main() { ...@@ -567,13 +567,10 @@ void main() {
final TestSemantics expected = new TestSemantics.root( final TestSemantics expected = new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 1,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
SemanticsFlag.isSelected, SemanticsFlag.isSelected,
SemanticsFlag.isHeader, SemanticsFlag.isHeader,
...@@ -583,7 +580,6 @@ void main() { ...@@ -583,7 +580,6 @@ void main() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
id: 4,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
SemanticsFlag.isHeader, SemanticsFlag.isHeader,
], ],
...@@ -592,7 +588,6 @@ void main() { ...@@ -592,7 +588,6 @@ void main() {
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
id: 5,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
SemanticsFlag.isHeader, SemanticsFlag.isHeader,
], ],
...@@ -606,7 +601,75 @@ void main() { ...@@ -606,7 +601,75 @@ void main() {
), ),
], ],
); );
expect(semantics, hasSemantics(expected, ignoreTransform: true, ignoreRect: true)); expect(semantics, hasSemantics(expected, ignoreId: true, ignoreTransform: true, ignoreRect: true));
semantics.dispose();
});
testWidgets('BottomNavigationBar.shifting semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
boilerplate(
textDirection: TextDirection.ltr,
bottomNavigationBar: new BottomNavigationBar(
type: BottomNavigationBarType.shifting,
items: const <BottomNavigationBarItem>[
const BottomNavigationBarItem(
icon: const Icon(Icons.ac_unit),
title: const Text('AC'),
),
const BottomNavigationBarItem(
icon: const Icon(Icons.access_alarm),
title: const Text('Alarm'),
),
const BottomNavigationBarItem(
icon: const Icon(Icons.hot_tub),
title: const Text('Hot Tub'),
),
],
),
),
);
final TestSemantics expected = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isSelected,
SemanticsFlag.isHeader,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'AC\nTab 1 of 3',
textDirection: TextDirection.ltr,
),
new TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isHeader,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Alarm\nTab 2 of 3',
textDirection: TextDirection.ltr,
),
new TestSemantics(
flags: <SemanticsFlag>[
SemanticsFlag.isHeader,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Hot Tub\nTab 3 of 3',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
);
expect(semantics, hasSemantics(expected, ignoreId: true, ignoreTransform: true, ignoreRect: true));
semantics.dispose(); semantics.dispose();
}); });
......
...@@ -219,6 +219,7 @@ void main() { ...@@ -219,6 +219,7 @@ void main() {
)..value = 0.0; )..value = 0.0;
final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity( final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity(
alwaysIncludeSemantics: false,
opacity: opacityAnimation, opacity: opacityAnimation,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
); );
...@@ -233,6 +234,7 @@ void main() { ...@@ -233,6 +234,7 @@ void main() {
)..value = 1.0; )..value = 1.0;
final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity( final RenderAnimatedOpacity renderAnimatedOpacity = new RenderAnimatedOpacity(
alwaysIncludeSemantics: false,
opacity: opacityAnimation, opacity: opacityAnimation,
child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter child: new RenderSizedBox(const Size(1.0, 1.0)), // size doesn't matter
); );
......
...@@ -43,6 +43,7 @@ void main() { ...@@ -43,6 +43,7 @@ void main() {
test('RenderOpacity and children and semantics', () { test('RenderOpacity and children and semantics', () {
final AnimationController controller = new AnimationController(vsync: const TestVSync()); final AnimationController controller = new AnimationController(vsync: const TestVSync());
final RenderAnimatedOpacity box = new RenderAnimatedOpacity( final RenderAnimatedOpacity box = new RenderAnimatedOpacity(
alwaysIncludeSemantics: false,
opacity: controller, opacity: controller,
child: new RenderParagraph( child: new RenderParagraph(
const TextSpan(), const TextSpan(),
......
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