Unverified Commit b54b576c authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Assorted a11y fixes (#14395)

parent 882be89e
...@@ -19,23 +19,27 @@ class SectionCard extends StatelessWidget { ...@@ -19,23 +19,27 @@ class SectionCard extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return new DecoratedBox( return new Semantics(
decoration: new BoxDecoration( label: section.title,
gradient: new LinearGradient( button: true,
begin: Alignment.centerLeft, child: new DecoratedBox(
end: Alignment.centerRight, decoration: new BoxDecoration(
colors: <Color>[ gradient: new LinearGradient(
section.leftColor, begin: Alignment.centerLeft,
section.rightColor, end: Alignment.centerRight,
], colors: <Color>[
section.leftColor,
section.rightColor,
],
),
),
child: new Image.asset(
section.backgroundAsset,
package: section.backgroundAssetPackage,
color: const Color.fromRGBO(255, 255, 255, 0.075),
colorBlendMode: BlendMode.modulate,
fit: BoxFit.cover,
), ),
),
child: new Image.asset(
section.backgroundAsset,
package: section.backgroundAssetPackage,
color: const Color.fromRGBO(255, 255, 255, 0.075),
colorBlendMode: BlendMode.modulate,
fit: BoxFit.cover,
), ),
); );
} }
......
...@@ -107,6 +107,7 @@ class ExitButton extends StatelessWidget { ...@@ -107,6 +107,7 @@ class ExitButton extends StatelessWidget {
child: const Tooltip( child: const Tooltip(
message: 'Back', message: 'Back',
child: const Text('Exit'), child: const Text('Exit'),
excludeFromSemantics: true,
), ),
onPressed: () { onPressed: () {
// The demo is on the root navigator. // The demo is on the root navigator.
......
...@@ -218,14 +218,20 @@ class _ButtonsDemoState extends State<ButtonsDemo> { ...@@ -218,14 +218,20 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
new IconButton( new IconButton(
icon: const Icon(Icons.thumb_up), icon: const Icon(
Icons.thumb_up,
semanticLabel: 'Thumbs up',
),
onPressed: () { onPressed: () {
setState(() => iconButtonToggle = !iconButtonToggle); setState(() => iconButtonToggle = !iconButtonToggle);
}, },
color: iconButtonToggle ? Theme.of(context).primaryColor : null, color: iconButtonToggle ? Theme.of(context).primaryColor : null,
), ),
const IconButton( const IconButton(
icon: const Icon(Icons.thumb_up), icon: const Icon(
Icons.thumb_up,
semanticLabel: 'Thumbs up',
),
onPressed: null, onPressed: null,
) )
] ]
......
...@@ -108,21 +108,24 @@ class _IconsDemoCard extends StatelessWidget { ...@@ -108,21 +108,24 @@ class _IconsDemoCard extends StatelessWidget {
return new Card( return new Card(
child: new DefaultTextStyle( child: new DefaultTextStyle(
style: textStyle, style: textStyle,
child: new Table( child: new Semantics(
defaultVerticalAlignment: TableCellVerticalAlignment.middle, explicitChildNodes: true,
children: <TableRow> [ child: new Table(
new TableRow( defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <Widget> [ children: <TableRow> [
_centeredText('Size'), new TableRow(
_centeredText('Enabled'), children: <Widget> [
_centeredText('Disabled'), _centeredText('Size'),
] _centeredText('Enabled'),
), _centeredText('Disabled'),
_buildIconRow(18.0), ]
_buildIconRow(24.0), ),
_buildIconRow(36.0), _buildIconRow(18.0),
_buildIconRow(48.0), _buildIconRow(24.0),
], _buildIconRow(36.0),
_buildIconRow(48.0),
],
),
), ),
), ),
); );
......
...@@ -76,7 +76,10 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> { ...@@ -76,7 +76,10 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> {
floatingActionButton: new FloatingActionButton( floatingActionButton: new FloatingActionButton(
onPressed: _showMessage, onPressed: _showMessage,
backgroundColor: Colors.redAccent, backgroundColor: Colors.redAccent,
child: const Icon(Icons.add) child: const Icon(
Icons.add,
semanticLabel: 'Add',
),
), ),
body: new Center( body: new Center(
child: new RaisedButton( child: new RaisedButton(
......
...@@ -154,7 +154,10 @@ class FullScreenCodeDialogState extends State<FullScreenCodeDialog> { ...@@ -154,7 +154,10 @@ class FullScreenCodeDialogState extends State<FullScreenCodeDialog> {
return new Scaffold( return new Scaffold(
appBar: new AppBar( appBar: new AppBar(
leading: new IconButton( leading: new IconButton(
icon: const Icon(Icons.clear), icon: const Icon(
Icons.clear,
semanticLabel: 'Close',
),
onPressed: () { Navigator.pop(context); } onPressed: () { Navigator.pop(context); }
), ),
title: const Text('Example code') title: const Text('Example code')
......
...@@ -52,51 +52,54 @@ class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> { ...@@ -52,51 +52,54 @@ class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double systemTopPadding = MediaQuery.of(context).padding.top; final double systemTopPadding = MediaQuery.of(context).padding.top;
return new DrawerHeader( return new Semantics(
decoration: new FlutterLogoDecoration( label: 'Flutter',
margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0), child: new DrawerHeader(
style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal decoration: new FlutterLogoDecoration(
: FlutterLogoStyle.stacked margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0),
: FlutterLogoStyle.markOnly, style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal
lightColor: _logoColor.shade400, : FlutterLogoStyle.stacked
darkColor: _logoColor.shade900, : FlutterLogoStyle.markOnly,
textColor: widget.light ? const Color(0xFF616161) : const Color(0xFF9E9E9E), lightColor: _logoColor.shade400,
darkColor: _logoColor.shade900,
textColor: widget.light ? const Color(0xFF616161) : const Color(0xFF9E9E9E),
),
duration: const Duration(milliseconds: 750),
child: new GestureDetector(
onLongPress: () {
setState(() {
_logoHorizontal = !_logoHorizontal;
if (!_logoHasName)
_logoHasName = true;
});
},
onTap: () {
setState(() {
_logoHasName = !_logoHasName;
});
},
onDoubleTap: () {
setState(() {
final List<MaterialColor> options = <MaterialColor>[];
if (_logoColor != Colors.blue)
options.addAll(<MaterialColor>[Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue]);
if (_logoColor != Colors.amber)
options.addAll(<MaterialColor>[Colors.amber, Colors.amber, Colors.amber]);
if (_logoColor != Colors.red)
options.addAll(<MaterialColor>[Colors.red, Colors.red, Colors.red]);
if (_logoColor != Colors.indigo)
options.addAll(<MaterialColor>[Colors.indigo, Colors.indigo, Colors.indigo]);
if (_logoColor != Colors.pink)
options.addAll(<MaterialColor>[Colors.pink]);
if (_logoColor != Colors.purple)
options.addAll(<MaterialColor>[Colors.purple]);
if (_logoColor != Colors.cyan)
options.addAll(<MaterialColor>[Colors.cyan]);
_logoColor = options[new math.Random().nextInt(options.length)];
});
}
),
), ),
duration: const Duration(milliseconds: 750),
child: new GestureDetector(
onLongPress: () {
setState(() {
_logoHorizontal = !_logoHorizontal;
if (!_logoHasName)
_logoHasName = true;
});
},
onTap: () {
setState(() {
_logoHasName = !_logoHasName;
});
},
onDoubleTap: () {
setState(() {
final List<MaterialColor> options = <MaterialColor>[];
if (_logoColor != Colors.blue)
options.addAll(<MaterialColor>[Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue, Colors.blue]);
if (_logoColor != Colors.amber)
options.addAll(<MaterialColor>[Colors.amber, Colors.amber, Colors.amber]);
if (_logoColor != Colors.red)
options.addAll(<MaterialColor>[Colors.red, Colors.red, Colors.red]);
if (_logoColor != Colors.indigo)
options.addAll(<MaterialColor>[Colors.indigo, Colors.indigo, Colors.indigo]);
if (_logoColor != Colors.pink)
options.addAll(<MaterialColor>[Colors.pink]);
if (_logoColor != Colors.purple)
options.addAll(<MaterialColor>[Colors.purple]);
if (_logoColor != Colors.cyan)
options.addAll(<MaterialColor>[Colors.cyan]);
_logoColor = options[new math.Random().nextInt(options.length)];
});
}
)
); );
} }
} }
......
...@@ -165,10 +165,14 @@ class _FloatingActionButtonState extends State<FloatingActionButton> { ...@@ -165,10 +165,14 @@ class _FloatingActionButtonState extends State<FloatingActionButton> {
child: new Container( child: new Container(
width: widget.mini ? _kSizeMini : _kSize, width: widget.mini ? _kSizeMini : _kSize,
height: widget.mini ? _kSizeMini : _kSize, height: widget.mini ? _kSizeMini : _kSize,
child: new InkWell( child: new Semantics(
onTap: widget.onPressed, button: true,
onHighlightChanged: _handleHighlightChanged, enabled: widget.onPressed != null,
child: result, child: new InkWell(
onTap: widget.onPressed,
onHighlightChanged: _handleHighlightChanged,
child: result,
),
), ),
), ),
); );
......
...@@ -193,6 +193,7 @@ class IconButton extends StatelessWidget { ...@@ -193,6 +193,7 @@ class IconButton extends StatelessWidget {
Widget result = new Semantics( Widget result = new Semantics(
button: true, button: true,
enabled: onPressed != null,
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: const BoxConstraints(minWidth: _kMinButtonSize, minHeight: _kMinButtonSize), constraints: const BoxConstraints(minWidth: _kMinButtonSize, minHeight: _kMinButtonSize),
child: new Padding( child: new Padding(
......
...@@ -48,12 +48,14 @@ class Tooltip extends StatefulWidget { ...@@ -48,12 +48,14 @@ class Tooltip extends StatefulWidget {
this.padding: const EdgeInsets.symmetric(horizontal: 16.0), this.padding: const EdgeInsets.symmetric(horizontal: 16.0),
this.verticalOffset: 24.0, this.verticalOffset: 24.0,
this.preferBelow: true, this.preferBelow: true,
this.excludeFromSemantics: false,
this.child, this.child,
}) : assert(message != null), }) : assert(message != null),
assert(height != null), assert(height != null),
assert(padding != null), assert(padding != null),
assert(verticalOffset != null), assert(verticalOffset != null),
assert(preferBelow != null), assert(preferBelow != null),
assert(excludeFromSemantics != null),
super(key: key); super(key: key);
/// The text to display in the tooltip. /// The text to display in the tooltip.
...@@ -77,6 +79,10 @@ class Tooltip extends StatefulWidget { ...@@ -77,6 +79,10 @@ class Tooltip extends StatefulWidget {
/// direction. /// direction.
final bool preferBelow; final bool preferBelow;
/// Whether the tooltip's [message] should be excluded from the semantics
/// tree.
final bool excludeFromSemantics;
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
/// ///
/// {@macro flutter.widgets.child} /// {@macro flutter.widgets.child}
...@@ -191,7 +197,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin { ...@@ -191,7 +197,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
onLongPress: _handleLongPress, onLongPress: _handleLongPress,
excludeFromSemantics: true, excludeFromSemantics: true,
child: new Semantics( child: new Semantics(
label: widget.message, label: widget.excludeFromSemantics ? null : widget.message,
child: widget.child, child: widget.child,
), ),
); );
......
...@@ -573,13 +573,21 @@ void _tests() { ...@@ -573,13 +573,21 @@ void _tests() {
], ],
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Previous month December 2015', label: r'Previous month December 2015',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Next month February 2016', label: r'Next month February 2016',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
......
...@@ -2,9 +2,14 @@ ...@@ -2,9 +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/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
void main() { void main() {
testWidgets('Floating Action Button control test', (WidgetTester tester) async { testWidgets('Floating Action Button control test', (WidgetTester tester) async {
bool didPressButton = false; bool didPressButton = false;
...@@ -132,4 +137,68 @@ void main() { ...@@ -132,4 +137,68 @@ void main() {
await tester.pump(); await tester.pump();
expect(tester.takeException().toString(), contains('xyzzy')); expect(tester.takeException().toString(), contains('xyzzy'));
}); });
testWidgets('Floating Action Button semantics (enabled)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Center(
child: new FloatingActionButton(
onPressed: () { },
child: const Icon(Icons.add, semanticLabel: 'Add'),
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'Add',
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[
SemanticsAction.tap
],
),
],
), ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
});
testWidgets('Floating Action Button semantics (disabled)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: const Center(
child: const FloatingActionButton(
onPressed: null,
child: const Icon(Icons.add, semanticLabel: 'Add'),
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'Add',
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
],
),
],
), ignoreTransform: true, ignoreId: true, ignoreRect: true));
semantics.dispose();
});
} }
...@@ -275,7 +275,7 @@ void main() { ...@@ -275,7 +275,7 @@ void main() {
await gesture.up(); await gesture.up();
}); });
testWidgets('IconButton Semantics', (WidgetTester tester) async { testWidgets('IconButton Semantics (enabled)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -291,8 +291,14 @@ void main() { ...@@ -291,8 +291,14 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
rect: new Rect.fromLTRB(0.0, 0.0, 48.0, 48.0), rect: new Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[
flags: <SemanticsFlag>[SemanticsFlag.isButton], SemanticsAction.tap
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
SemanticsFlag.isButton
],
label: 'link', label: 'link',
) )
] ]
...@@ -300,6 +306,34 @@ void main() { ...@@ -300,6 +306,34 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
testWidgets('IconButton Semantics (disabled)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
wrap(
child: const IconButton(
onPressed: null,
icon: const Icon(Icons.link, semanticLabel: 'link'),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
rect: new Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isButton
],
label: 'link',
)
]
), ignoreId: true, ignoreTransform: true));
semantics.dispose();
});
} }
Widget wrap({ Widget child }) { Widget wrap({ Widget child }) {
......
...@@ -602,4 +602,57 @@ void main() { ...@@ -602,4 +602,57 @@ void main() {
feedback.dispose(); feedback.dispose();
}); });
testWidgets('Semantics included', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new MaterialApp(
home: const Center(
child: const Tooltip(
message: 'Foo',
child: const Text('Bar'),
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'Foo\nBar',
textDirection: TextDirection.ltr,
),
],
), ignoreRect: true, ignoreId: true, ignoreTransform: true));
semantics.dispose();
});
testWidgets('Semantics excluded', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new MaterialApp(
home: const Center(
child: const Tooltip(
message: 'Foo',
child: const Text('Bar'),
excludeFromSemantics: true,
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
label: 'Bar',
textDirection: TextDirection.ltr,
),
],
), ignoreRect: true, ignoreId: true, ignoreTransform: true));
semantics.dispose();
});
} }
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