Allow specifying and a11y label for Icon widget (#12475)
* Allow specifying and a11y label for Icon widget
This commit is contained in:
parent
c38b843460
commit
5e595d12e4
@ -35,7 +35,8 @@ class Icon extends StatelessWidget {
|
||||
const Icon(this.icon, {
|
||||
Key key,
|
||||
this.size,
|
||||
this.color
|
||||
this.color,
|
||||
this.semanticLabel,
|
||||
}) : super(key: key);
|
||||
|
||||
/// The icon to display. The available icons are described in [Icons].
|
||||
@ -83,6 +84,14 @@ class Icon extends StatelessWidget {
|
||||
/// ```
|
||||
final Color color;
|
||||
|
||||
/// Semantic label for the icon.
|
||||
///
|
||||
/// This would be read out in accessibility modes (e.g TalkBack/VoiceOver).
|
||||
/// This label does not show in the UI.
|
||||
///
|
||||
/// See [Semantics.label];
|
||||
final String semanticLabel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(debugCheckHasDirectionality(context));
|
||||
@ -93,27 +102,29 @@ class Icon extends StatelessWidget {
|
||||
final double iconSize = size ?? iconTheme.size;
|
||||
|
||||
if (icon == null)
|
||||
return new SizedBox(width: iconSize, height: iconSize);
|
||||
return _wrapWithSemantics(new SizedBox(width: iconSize, height: iconSize));
|
||||
|
||||
final double iconOpacity = iconTheme.opacity;
|
||||
Color iconColor = color ?? iconTheme.color;
|
||||
if (iconOpacity != 1.0)
|
||||
iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);
|
||||
|
||||
return new ExcludeSemantics(
|
||||
child: new SizedBox(
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
child: new Center(
|
||||
child: new RichText(
|
||||
textDirection: textDirection, // Since we already fetched it for the assert...
|
||||
text: new TextSpan(
|
||||
text: new String.fromCharCode(icon.codePoint),
|
||||
style: new TextStyle(
|
||||
inherit: false,
|
||||
color: iconColor,
|
||||
fontSize: iconSize,
|
||||
fontFamily: icon.fontFamily,
|
||||
return _wrapWithSemantics(
|
||||
new ExcludeSemantics(
|
||||
child: new SizedBox(
|
||||
width: iconSize,
|
||||
height: iconSize,
|
||||
child: new Center(
|
||||
child: new RichText(
|
||||
textDirection: textDirection, // Since we already fetched it for the assert...
|
||||
text: new TextSpan(
|
||||
text: new String.fromCharCode(icon.codePoint),
|
||||
style: new TextStyle(
|
||||
inherit: false,
|
||||
color: iconColor,
|
||||
fontSize: iconSize,
|
||||
fontFamily: icon.fontFamily,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -122,6 +133,17 @@ class Icon extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// Wraps the widget with a Semantics widget if [semanticLabel] is set.
|
||||
Widget _wrapWithSemantics(Widget widget) {
|
||||
if (semanticLabel == null)
|
||||
return widget;
|
||||
|
||||
return new Semantics(
|
||||
child: widget,
|
||||
label: semanticLabel,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
|
@ -2,9 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'semantics_tester.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Can set opacity for an Icon', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
@ -122,4 +125,40 @@ void main() {
|
||||
final RichText richText = tester.firstWidget(find.byType(RichText));
|
||||
expect(richText.text.style.fontFamily, equals('Roboto'));
|
||||
});
|
||||
|
||||
testWidgets('Icon with semantic label', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: const Center(
|
||||
child: const Icon(
|
||||
Icons.title,
|
||||
semanticLabel: 'a label',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(semantics, hasSemantics(new TestSemantics.root(label: 'a label')));
|
||||
});
|
||||
|
||||
testWidgets('Null icon with semantic label', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
const Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: const Center(
|
||||
child: const Icon(
|
||||
null,
|
||||
semanticLabel: 'a label',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(semantics, hasSemantics(new TestSemantics.root(label: 'a label')));
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user