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 {
@override
Widget build(BuildContext context) {
return new DecoratedBox(
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: <Color>[
section.leftColor,
section.rightColor,
],
return new Semantics(
label: section.title,
button: true,
child: new DecoratedBox(
decoration: new BoxDecoration(
gradient: new LinearGradient(
begin: Alignment.centerLeft,
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 {
child: const Tooltip(
message: 'Back',
child: const Text('Exit'),
excludeFromSemantics: true,
),
onPressed: () {
// The demo is on the root navigator.
......
......@@ -218,14 +218,20 @@ class _ButtonsDemoState extends State<ButtonsDemo> {
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new IconButton(
icon: const Icon(Icons.thumb_up),
icon: const Icon(
Icons.thumb_up,
semanticLabel: 'Thumbs up',
),
onPressed: () {
setState(() => iconButtonToggle = !iconButtonToggle);
},
color: iconButtonToggle ? Theme.of(context).primaryColor : null,
),
const IconButton(
icon: const Icon(Icons.thumb_up),
icon: const Icon(
Icons.thumb_up,
semanticLabel: 'Thumbs up',
),
onPressed: null,
)
]
......
......@@ -108,21 +108,24 @@ class _IconsDemoCard extends StatelessWidget {
return new Card(
child: new DefaultTextStyle(
style: textStyle,
child: new Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow> [
new TableRow(
children: <Widget> [
_centeredText('Size'),
_centeredText('Enabled'),
_centeredText('Disabled'),
]
),
_buildIconRow(18.0),
_buildIconRow(24.0),
_buildIconRow(36.0),
_buildIconRow(48.0),
],
child: new Semantics(
explicitChildNodes: true,
child: new Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: <TableRow> [
new TableRow(
children: <Widget> [
_centeredText('Size'),
_centeredText('Enabled'),
_centeredText('Disabled'),
]
),
_buildIconRow(18.0),
_buildIconRow(24.0),
_buildIconRow(36.0),
_buildIconRow(48.0),
],
),
),
),
);
......
......@@ -76,7 +76,10 @@ class _PersistentBottomSheetDemoState extends State<PersistentBottomSheetDemo> {
floatingActionButton: new FloatingActionButton(
onPressed: _showMessage,
backgroundColor: Colors.redAccent,
child: const Icon(Icons.add)
child: const Icon(
Icons.add,
semanticLabel: 'Add',
),
),
body: new Center(
child: new RaisedButton(
......
......@@ -154,7 +154,10 @@ class FullScreenCodeDialogState extends State<FullScreenCodeDialog> {
return new Scaffold(
appBar: new AppBar(
leading: new IconButton(
icon: const Icon(Icons.clear),
icon: const Icon(
Icons.clear,
semanticLabel: 'Close',
),
onPressed: () { Navigator.pop(context); }
),
title: const Text('Example code')
......
......@@ -52,51 +52,54 @@ class _GalleryDrawerHeaderState extends State<GalleryDrawerHeader> {
Widget build(BuildContext context) {
final double systemTopPadding = MediaQuery.of(context).padding.top;
return new DrawerHeader(
decoration: new FlutterLogoDecoration(
margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0),
style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal
: FlutterLogoStyle.stacked
: FlutterLogoStyle.markOnly,
lightColor: _logoColor.shade400,
darkColor: _logoColor.shade900,
textColor: widget.light ? const Color(0xFF616161) : const Color(0xFF9E9E9E),
return new Semantics(
label: 'Flutter',
child: new DrawerHeader(
decoration: new FlutterLogoDecoration(
margin: new EdgeInsets.fromLTRB(12.0, 12.0 + systemTopPadding, 12.0, 12.0),
style: _logoHasName ? _logoHorizontal ? FlutterLogoStyle.horizontal
: FlutterLogoStyle.stacked
: FlutterLogoStyle.markOnly,
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> {
child: new Container(
width: widget.mini ? _kSizeMini : _kSize,
height: widget.mini ? _kSizeMini : _kSize,
child: new InkWell(
onTap: widget.onPressed,
onHighlightChanged: _handleHighlightChanged,
child: result,
child: new Semantics(
button: true,
enabled: widget.onPressed != null,
child: new InkWell(
onTap: widget.onPressed,
onHighlightChanged: _handleHighlightChanged,
child: result,
),
),
),
);
......
......@@ -193,6 +193,7 @@ class IconButton extends StatelessWidget {
Widget result = new Semantics(
button: true,
enabled: onPressed != null,
child: new ConstrainedBox(
constraints: const BoxConstraints(minWidth: _kMinButtonSize, minHeight: _kMinButtonSize),
child: new Padding(
......
......@@ -48,12 +48,14 @@ class Tooltip extends StatefulWidget {
this.padding: const EdgeInsets.symmetric(horizontal: 16.0),
this.verticalOffset: 24.0,
this.preferBelow: true,
this.excludeFromSemantics: false,
this.child,
}) : assert(message != null),
assert(height != null),
assert(padding != null),
assert(verticalOffset != null),
assert(preferBelow != null),
assert(excludeFromSemantics != null),
super(key: key);
/// The text to display in the tooltip.
......@@ -77,6 +79,10 @@ class Tooltip extends StatefulWidget {
/// direction.
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.
///
/// {@macro flutter.widgets.child}
......@@ -191,7 +197,7 @@ class _TooltipState extends State<Tooltip> with SingleTickerProviderStateMixin {
onLongPress: _handleLongPress,
excludeFromSemantics: true,
child: new Semantics(
label: widget.message,
label: widget.excludeFromSemantics ? null : widget.message,
child: widget.child,
),
);
......
......@@ -573,13 +573,21 @@ void _tests() {
],
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Previous month December 2015',
textDirection: TextDirection.ltr,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
flags: <SemanticsFlag>[
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Next month February 2016',
textDirection: TextDirection.ltr,
......
......@@ -2,9 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
void main() {
testWidgets('Floating Action Button control test', (WidgetTester tester) async {
bool didPressButton = false;
......@@ -132,4 +137,68 @@ void main() {
await tester.pump();
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() {
await gesture.up();
});
testWidgets('IconButton Semantics', (WidgetTester tester) async {
testWidgets('IconButton Semantics (enabled)', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
......@@ -291,8 +291,14 @@ void main() {
children: <TestSemantics>[
new TestSemantics.rootChild(
rect: new Rect.fromLTRB(0.0, 0.0, 48.0, 48.0),
actions: <SemanticsAction>[SemanticsAction.tap],
flags: <SemanticsFlag>[SemanticsFlag.isButton],
actions: <SemanticsAction>[
SemanticsAction.tap
],
flags: <SemanticsFlag>[
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
SemanticsFlag.isButton
],
label: 'link',
)
]
......@@ -300,6 +306,34 @@ void main() {
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 }) {
......
......@@ -602,4 +602,57 @@ void main() {
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