Unverified Commit 86be138d authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Make BottomNavBar accessible (#15211)

parent 0edea887
...@@ -13,6 +13,7 @@ import 'colors.dart'; ...@@ -13,6 +13,7 @@ import 'colors.dart';
import 'constants.dart'; import 'constants.dart';
import 'ink_well.dart'; import 'ink_well.dart';
import 'material.dart'; import 'material.dart';
import 'material_localizations.dart';
import 'theme.dart'; import 'theme.dart';
import 'typography.dart'; import 'typography.dart';
...@@ -129,9 +130,11 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -129,9 +130,11 @@ class _BottomNavigationTile extends StatelessWidget {
this.iconSize, { this.iconSize, {
this.onTap, this.onTap,
this.colorTween, this.colorTween,
this.flex this.flex,
this.selected: false,
this.indexLabel,
} }
); ): assert(selected != null);
final BottomNavigationBarType type; final BottomNavigationBarType type;
final BottomNavigationBarItem item; final BottomNavigationBarItem item;
...@@ -140,6 +143,8 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -140,6 +143,8 @@ class _BottomNavigationTile extends StatelessWidget {
final VoidCallback onTap; final VoidCallback onTap;
final ColorTween colorTween; final ColorTween colorTween;
final double flex; final double flex;
final bool selected;
final String indexLabel;
Widget _buildIcon() { Widget _buildIcon() {
double tweenStart; double tweenStart;
...@@ -255,7 +260,12 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -255,7 +260,12 @@ class _BottomNavigationTile extends StatelessWidget {
} }
return new Expanded( return new Expanded(
flex: size, flex: size,
child: new InkResponse( child: new Semantics(
container: true,
selected: selected,
child: new Stack(
children: <Widget>[
new InkResponse(
onTap: onTap, onTap: onTap,
child: new Column( child: new Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
...@@ -267,6 +277,12 @@ class _BottomNavigationTile extends StatelessWidget { ...@@ -267,6 +277,12 @@ class _BottomNavigationTile extends StatelessWidget {
], ],
), ),
), ),
new Semantics(
label: indexLabel,
)
],
),
),
); );
} }
} }
...@@ -368,6 +384,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -368,6 +384,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
} }
List<Widget> _createTiles() { List<Widget> _createTiles() {
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
assert(localizations != null);
final List<Widget> children = <Widget>[]; final List<Widget> children = <Widget>[];
switch (widget.type) { switch (widget.type) {
case BottomNavigationBarType.fixed: case BottomNavigationBarType.fixed:
...@@ -398,6 +416,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -398,6 +416,8 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
widget.onTap(i); widget.onTap(i);
}, },
colorTween: colorTween, colorTween: colorTween,
selected: i == widget.currentIndex,
indexLabel: localizations.tabLabel(tabIndex: i + 1, tabCount: widget.items.length),
), ),
); );
} }
...@@ -415,7 +435,9 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -415,7 +435,9 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
widget.onTap(i); widget.onTap(i);
}, },
flex: _evaluateFlex(_animations[i]), flex: _evaluateFlex(_animations[i]),
), selected: i == widget.currentIndex,
indexLabel: localizations.tabLabel(tabIndex: i + 1, tabCount: widget.items.length),
)
); );
} }
break; break;
......
...@@ -2,11 +2,14 @@ ...@@ -2,11 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart'; import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
void main() { void main() {
testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async { testWidgets('BottomNavigationBar callback test', (WidgetTester tester) async {
...@@ -483,11 +486,78 @@ void main() { ...@@ -483,11 +486,78 @@ void main() {
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
expect(box, paints..circle(x: 600.0)..circle(x: 200.0)..circle(x: 600.0)); expect(box, paints..circle(x: 600.0)..circle(x: 200.0)..circle(x: 600.0));
}); });
testWidgets('BottomNavigationBar semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
boilerplate(
textDirection: TextDirection.ltr,
bottomNavigationBar: new BottomNavigationBar(
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'),
),
],
),
),
);
// TODO(goderbauer): traversal order is incorrect, https://github.com/flutter/flutter/issues/14375
final TestSemantics expected = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics(
id: 1,
flags: <SemanticsFlag>[SemanticsFlag.isSelected],
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'AC\nTab 1 of 3',
textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: 3, // Should be 2
),
new TestSemantics(
id: 2,
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Alarm\nTab 2 of 3',
textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: -1, // Should be 1
),
new TestSemantics(
id: 3,
actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Hot Tub\nTab 3 of 3',
textDirection: TextDirection.ltr,
nextNodeId: 1, // Should be -1
previousNodeId: 2,
),
],
);
expect(semantics, hasSemantics(expected, ignoreTransform: true, ignoreRect: true));
semantics.dispose();
});
} }
Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDirection }) { Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDirection }) {
assert(textDirection != null); assert(textDirection != null);
return new Directionality( return new Localizations(
locale: const Locale('en', 'US'),
delegates: <LocalizationsDelegate<dynamic>>[
DefaultMaterialLocalizations.delegate,
DefaultWidgetsLocalizations.delegate,
],
child: new Directionality(
textDirection: textDirection, textDirection: textDirection,
child: new MediaQuery( child: new MediaQuery(
data: const MediaQueryData(), data: const MediaQueryData(),
...@@ -497,5 +567,6 @@ Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDir ...@@ -497,5 +567,6 @@ Widget boilerplate({ Widget bottomNavigationBar, @required TextDirection textDir
), ),
), ),
), ),
),
); );
} }
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