Unverified Commit 63f8b9a4 authored by Kate Lovett's avatar Kate Lovett Committed by GitHub

Improving A11y for Flutter Gallery Demos (#53616)

parent 85ad6998
......@@ -323,9 +323,7 @@ class _Heading extends StatelessWidget {
alignment: AlignmentDirectional.centerStart,
child: Text(
text,
style: theme.textTheme.bodyText2.copyWith(
color: theme.primaryColor,
),
style: theme.textTheme.bodyText1,
),
);
}
......
......@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
import '../../gallery/demo.dart';
const List<String> _defaultMaterials = <String>[
const List<String> _defaultMaterialsA = <String>[
'poker',
'tortilla',
'fish and',
......@@ -14,6 +14,14 @@ const List<String> _defaultMaterials = <String>[
'wood',
];
const List<String> _defaultMaterialsB = <String>[
'apple',
'orange',
'tomato',
'grape',
'lettuce',
];
const List<String> _defaultActions = <String>[
'flake',
'cut',
......@@ -38,7 +46,7 @@ const Map<String, String> _results = <String, String>{
'eat': 'eating',
};
const List<String> _defaultTools = <String>[
const List<String> _defaultToolsA = <String>[
'hammer',
'chisel',
'fryer',
......@@ -46,6 +54,14 @@ const List<String> _defaultTools = <String>[
'customer',
];
const List<String> _defaultToolsB = <String>[
'keyboard',
'mouse',
'monitor',
'printer',
'cable',
];
const Map<String, String> _avatars = <String, String>{
'hammer': 'people/square/ali.png',
'chisel': 'people/square/sandra.png',
......@@ -130,36 +146,44 @@ class _ChipDemoState extends State<ChipDemo> {
_reset();
}
final Set<String> _materials = <String>{};
final Set<String> _materialsA = <String>{};
final Set<String> _materialsB = <String>{};
String _selectedMaterial = '';
String _selectedAction = '';
final Set<String> _tools = <String>{};
final Set<String> _toolsA = <String>{};
final Set<String> _toolsB = <String>{};
final Set<String> _selectedTools = <String>{};
final Set<String> _actions = <String>{};
bool _showShapeBorder = false;
// Initialize members with the default data.
void _reset() {
_materials.clear();
_materials.addAll(_defaultMaterials);
_materialsA.clear();
_materialsA.addAll(_defaultMaterialsA);
_materialsB.clear();
_materialsB.addAll(_defaultMaterialsB);
_actions.clear();
_actions.addAll(_defaultActions);
_tools.clear();
_tools.addAll(_defaultTools);
_toolsA.clear();
_toolsA.addAll(_defaultToolsA);
_toolsB.clear();
_toolsB.addAll(_defaultToolsB);
_selectedMaterial = '';
_selectedAction = '';
_selectedTools.clear();
}
void _removeMaterial(String name) {
_materials.remove(name);
_materialsA.remove(name);
_materialsB.remove(name);
if (_selectedMaterial == name) {
_selectedMaterial = '';
}
}
void _removeTool(String name) {
_tools.remove(name);
_toolsA.remove(name);
_toolsB.remove(name);
_selectedTools.remove(name);
}
......@@ -174,11 +198,12 @@ class _ChipDemoState extends State<ChipDemo> {
// saturation and value). This means that any unique strings will also have
// unique colors, but they'll all be readable, since they have the same
// saturation and value.
Color _nameToColor(String name) {
Color _nameToColor(String name, ThemeData theme) {
assert(name.length > 1);
final int hash = name.hashCode & 0xffff;
final double hue = (360.0 * hash / (1 << 15)) % 360.0;
return HSVColor.fromAHSV(1.0, hue, 0.4, 0.90).toColor();
final double themeValue = HSVColor.fromColor(theme.backgroundColor).value;
return HSVColor.fromAHSV(1.0, hue, 0.4, themeValue).toColor();
}
AssetImage _nameToAvatar(String name) {
......@@ -198,10 +223,11 @@ class _ChipDemoState extends State<ChipDemo> {
@override
Widget build(BuildContext context) {
final List<Widget> chips = _materials.map<Widget>((String name) {
final ThemeData theme = Theme.of(context);
final List<Widget> chips = _materialsA.map<Widget>((String name) {
return Chip(
key: ValueKey<String>(name),
backgroundColor: _nameToColor(name),
backgroundColor: _nameToColor(name, theme),
label: Text(_capitalize(name)),
onDeleted: () {
setState(() {
......@@ -211,7 +237,7 @@ class _ChipDemoState extends State<ChipDemo> {
);
}).toList();
final List<Widget> inputChips = _tools.map<Widget>((String name) {
final List<Widget> inputChips = _toolsA.map<Widget>((String name) {
return InputChip(
key: ValueKey<String>(name),
avatar: CircleAvatar(
......@@ -225,10 +251,10 @@ class _ChipDemoState extends State<ChipDemo> {
});
}).toList();
final List<Widget> choiceChips = _materials.map<Widget>((String name) {
final List<Widget> choiceChips = _materialsB.map<Widget>((String name) {
return ChoiceChip(
key: ValueKey<String>(name),
backgroundColor: _nameToColor(name),
backgroundColor: _nameToColor(name, theme),
label: Text(_capitalize(name)),
selected: _selectedMaterial == name,
onSelected: (bool value) {
......@@ -239,12 +265,12 @@ class _ChipDemoState extends State<ChipDemo> {
);
}).toList();
final List<Widget> filterChips = _defaultTools.map<Widget>((String name) {
final List<Widget> filterChips = _toolsB.map<Widget>((String name) {
return FilterChip(
key: ValueKey<String>(name),
label: Text(_capitalize(name)),
selected: _tools.contains(name) && _selectedTools.contains(name),
onSelected: !_tools.contains(name)
selected: _toolsB.contains(name) && _selectedTools.contains(name),
onSelected: !_toolsB.contains(name)
? null
: (bool value) {
setState(() {
......@@ -277,7 +303,6 @@ class _ChipDemoState extends State<ChipDemo> {
);
}).toList();
final ThemeData theme = Theme.of(context);
final List<Widget> tiles = <Widget>[
const SizedBox(height: 8.0, width: 0.0),
_ChipsTile(label: 'Available Materials (Chip)', children: chips),
......
......@@ -27,7 +27,7 @@ class DessertDataSource extends DataTableSource {
Dessert('Ice cream sandwich', 237, 9.0, 37, 4.3, 129, 8, 1),
Dessert('Eclair', 262, 16.0, 24, 6.0, 337, 6, 7),
Dessert('Cupcake', 305, 3.7, 67, 4.3, 413, 3, 8),
Dessert('Gingerbread', 356, 16.0, 49, 3.9, 327, 7, 16),
Dessert('Gingerbread', 356, 15.0, 49, 3.9, 327, 7, 16),
Dessert('Jelly bean', 375, 0.0, 94, 0.0, 50, 0, 0),
Dessert('Lollipop', 392, 0.2, 98, 0.0, 38, 0, 2),
Dessert('Honeycomb', 408, 3.2, 87, 6.5, 562, 0, 45),
......@@ -36,9 +36,9 @@ class DessertDataSource extends DataTableSource {
Dessert('Frozen yogurt with sugar', 168, 6.0, 26, 4.0, 87, 14, 1),
Dessert('Ice cream sandwich with sugar', 246, 9.0, 39, 4.3, 129, 8, 1),
Dessert('Eclair with sugar', 271, 16.0, 26, 6.0, 337, 6, 7),
Dessert('Eclair with sugar', 271, 14.0, 26, 6.0, 337, 6, 7),
Dessert('Cupcake with sugar', 314, 3.7, 69, 4.3, 413, 3, 8),
Dessert('Gingerbread with sugar', 345, 16.0, 51, 3.9, 327, 7, 16),
Dessert('Gingerbread with sugar', 345, 13.0, 51, 3.9, 327, 7, 16),
Dessert('Jelly bean with sugar', 364, 0.0, 96, 0.0, 50, 0, 0),
Dessert('Lollipop with sugar', 401, 0.2, 100, 0.0, 38, 0, 2),
Dessert('Honeycomb with sugar', 417, 3.2, 89, 6.5, 562, 0, 45),
......@@ -47,9 +47,9 @@ class DessertDataSource extends DataTableSource {
Dessert('Frozen yogurt with honey', 223, 6.0, 36, 4.0, 87, 14, 1),
Dessert('Ice cream sandwich with honey', 301, 9.0, 49, 4.3, 129, 8, 1),
Dessert('Eclair with honey', 326, 16.0, 36, 6.0, 337, 6, 7),
Dessert('Eclair with honey', 326, 12.0, 36, 6.0, 337, 6, 7),
Dessert('Cupcake with honey', 369, 3.7, 79, 4.3, 413, 3, 8),
Dessert('Gingerbread with honey', 420, 16.0, 61, 3.9, 327, 7, 16),
Dessert('Gingerbread with honey', 420, 11.0, 61, 3.9, 327, 7, 16),
Dessert('Jelly bean with honey', 439, 0.0, 106, 0.0, 50, 0, 0),
Dessert('Lollipop with honey', 456, 0.2, 110, 0.0, 38, 0, 2),
Dessert('Honeycomb with honey', 472, 3.2, 99, 6.5, 562, 0, 45),
......
......@@ -125,7 +125,7 @@ class _DateAndTimePickerDemoState extends State<DateAndTimePickerDemo> {
DateTime _fromDate = DateTime.now();
TimeOfDay _fromTime = const TimeOfDay(hour: 7, minute: 28);
DateTime _toDate = DateTime.now();
TimeOfDay _toTime = const TimeOfDay(hour: 7, minute: 28);
TimeOfDay _toTime = const TimeOfDay(hour: 8, minute: 28);
final List<String> _allActivities = <String>['hiking', 'swimming', 'boating', 'fishing'];
String _activity = 'fishing';
......
......@@ -54,6 +54,7 @@ class _ElevationDemoState extends State<ElevationDemo> {
actions: <Widget>[
MaterialDemoDocumentationButton(ElevationDemo.routeName),
IconButton(
tooltip: 'Toggle elevation',
icon: const Icon(Icons.sentiment_very_satisfied),
onPressed: () {
setState(() => _showElevation = !_showElevation);
......
......@@ -188,15 +188,18 @@ class GridDemoPhotoItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final Widget image = GestureDetector(
onTap: () { showPhoto(context); },
child: Hero(
key: Key(photo.assetName),
tag: photo.tag,
child: Image.asset(
photo.assetName,
package: photo.assetPackage,
fit: BoxFit.cover,
final Widget image = Semantics(
label: '${photo.title} - ${photo.caption}',
child: GestureDetector(
onTap: () { showPhoto(context); },
child: Hero(
key: Key(photo.assetName),
tag: photo.tag,
child: Image.asset(
photo.assetName,
package: photo.assetPackage,
fit: BoxFit.cover,
),
),
),
);
......
......@@ -99,7 +99,7 @@ class _IconsDemoCard extends StatelessWidget {
TableRow _buildIconRow(double size) {
return TableRow(
children: <Widget> [
_centeredText(size.floor().toString()),
_centeredText('${size.floor().toString()} ${icon.toString()}'),
_buildIconButton(size, icon, true),
_buildIconButton(size, icon, false),
],
......@@ -120,9 +120,9 @@ class _IconsDemoCard extends StatelessWidget {
children: <TableRow> [
TableRow(
children: <Widget> [
_centeredText('Size'),
_centeredText('Enabled'),
_centeredText('Disabled'),
_centeredText('Size ${icon.toString()}'),
_centeredText('Enabled ${icon.toString()}'),
_centeredText('Disabled ${icon.toString()}'),
]
),
_buildIconRow(18.0),
......
......@@ -129,6 +129,7 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> with SingleTicke
actions: <Widget>[
MaterialDemoDocumentationButton(ScrollableTabsDemo.routeName),
IconButton(
tooltip: 'Custom Indicator',
icon: const Icon(Icons.sentiment_very_satisfied),
onPressed: () {
setState(() {
......@@ -137,6 +138,7 @@ class ScrollableTabsDemoState extends State<ScrollableTabsDemo> with SingleTicke
},
),
PopupMenuButton<TabsDemoStyle>(
tooltip: 'Popup Menu',
onSelected: changeDemoStyle,
itemBuilder: (BuildContext context) => <PopupMenuItem<TabsDemoStyle>>[
const PopupMenuItem<TabsDemoStyle>(
......
......@@ -88,30 +88,39 @@ class _SelectionControlsDemoState extends State<SelectionControlsDemo> {
Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Checkbox(
value: checkboxValueA,
onChanged: (bool value) {
setState(() {
checkboxValueA = value;
});
},
Semantics(
label: 'Checkbox A',
child: Checkbox(
value: checkboxValueA,
onChanged: (bool value) {
setState(() {
checkboxValueA = value;
});
},
),
),
Checkbox(
value: checkboxValueB,
onChanged: (bool value) {
setState(() {
checkboxValueB = value;
});
},
Semantics(
label: 'Checkbox B',
child: Checkbox(
value: checkboxValueB,
onChanged: (bool value) {
setState(() {
checkboxValueB = value;
});
},
),
),
Checkbox(
value: checkboxValueC,
tristate: true,
onChanged: (bool value) {
setState(() {
checkboxValueC = value;
});
},
Semantics(
label: 'Checkbox C',
child: Checkbox(
value: checkboxValueC,
tristate: true,
onChanged: (bool value) {
setState(() {
checkboxValueC = value;
});
},
),
),
],
),
......
......@@ -27,7 +27,7 @@ class _Page {
}
final List<_Page> _allPages = <_Page>[
_Page(label: 'Blue', colors: Colors.indigo, icon: Icons.add),
_Page(label: 'Pink', colors: Colors.pink, icon: Icons.add),
_Page(label: 'Eco', colors: Colors.green, icon: Icons.create),
_Page(label: 'No'),
_Page(label: 'Teal', colors: Colors.teal, icon: Icons.add),
......
......@@ -50,7 +50,7 @@ void main() {
await tester.pumpWidget(MaterialApp(home: ChipDemo()));
await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
handle.dispose();
}, skip: true); // TODO(gspencergoog): Stop skipping when issue is fixed. https://github.com/flutter/flutter/issues/42455
}, skip: true); // https://github.com/flutter/flutter/issues/42455
testWidgets('data_table_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -290,7 +290,7 @@ void main() {
await tester.pumpWidget(MaterialApp(home: DataTableDemo()));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
handle.dispose();
}, skip: true);
}, skip: true); // DataTables are not accessible, https://github.com/flutter/flutter/issues/10830
testWidgets('date_and_time_picker_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -318,7 +318,7 @@ void main() {
await tester.pumpWidget(MaterialApp(home: ElevationDemo()));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
handle.dispose();
}, skip: true);
});
testWidgets('expansion_panels_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -332,7 +332,7 @@ void main() {
await tester.pumpWidget(const MaterialApp(home: GridListDemo()));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
handle.dispose();
}, skip: true);
});
testWidgets('icons_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -409,7 +409,7 @@ void main() {
await tester.pumpWidget(MaterialApp(home: ScrollableTabsDemo()));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
handle.dispose();
}, skip: true);
});
testWidgets('search_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -423,7 +423,7 @@ void main() {
await tester.pumpWidget(MaterialApp(home: SelectionControlsDemo()));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
handle.dispose();
}, skip: true);
});
testWidgets('slider_demo', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
......@@ -479,13 +479,13 @@ void main() {
final List<ThemeData> themes = <ThemeData>[
kLightGalleryTheme,
ThemeData.light(),
// TODO(hansmuller): add kDarkGalleryTheme, ThemeData.dark(), see #22044
ThemeData.dark(),
];
const List<String> themeNames = <String>[
'kLightGalleryTheme',
'ThemeData.light()',
// TODO(hansmuller): add 'kDarkGalleryTheme', 'ThemeData.dark()', see #22044
'ThemeData.dark()',
];
for (int themeIndex = 0; themeIndex < themes.length; themeIndex += 1) {
......@@ -506,7 +506,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: BottomAppBarDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: theme == ThemeData.light());
});
testWidgets('bottom_navigation_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -538,7 +538,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: ChipDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/21647
});
testWidgets('data_table_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -546,7 +546,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: DataTableDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/21647
});
testWidgets('date_and_time_picker_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -554,7 +554,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: DateAndTimePickerDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/21647
});
testWidgets('dialog_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -562,7 +562,9 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: DialogDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}, skip: theme == ThemeData.dark()); // Raised Button does not follow
// theme. https://github.com/flutter/flutter/issues/16488,
// https://github.com/flutter/flutter/issues/19623
testWidgets('drawer_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -602,7 +604,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: IconsDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/21647
});
testWidgets('leave_behind_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -631,10 +633,14 @@ void main() {
testWidgets('modal_bottom_sheet_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(MaterialApp(theme: theme, home: ModalBottomSheetDemo()));
await tester.pumpWidget(
MaterialApp(theme: theme, home: ModalBottomSheetDemo())
);
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}, skip: theme == ThemeData.dark()); // Raised Button does not follow
// theme. https://github.com/flutter/flutter/issues/16488,
// https://github.com/flutter/flutter/issues/19623
testWidgets('overscroll_demo', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -655,10 +661,14 @@ void main() {
testWidgets('persistent_bottom_sheet_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(MaterialApp(theme: theme, home: PersistentBottomSheetDemo()));
await tester.pumpWidget(
MaterialApp(theme: theme, home: PersistentBottomSheetDemo())
);
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}, skip: theme == ThemeData.dark()); // Raised Button does not follow
// theme. https://github.com/flutter/flutter/issues/16488,
// https://github.com/flutter/flutter/issues/19623
testWidgets('progress_indicator_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -690,7 +700,7 @@ void main() {
await tester.pumpWidget(MaterialApp(theme: theme, home: SearchDemo()));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
}, skip: true); // https://github.com/flutter/flutter/issues/21651
});
testWidgets('selection_controls_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......@@ -711,10 +721,14 @@ void main() {
testWidgets('snack_bar_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(MaterialApp(theme: theme, home: const SnackBarDemo()));
await tester.pumpWidget(
MaterialApp(theme: theme, home: const SnackBarDemo())
);
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}, skip: theme == ThemeData.dark()); // Raised Button does not follow
// theme. https://github.com/flutter/flutter/issues/16488,
// https://github.com/flutter/flutter/issues/19623
testWidgets('tabs_demo $themeName', (WidgetTester tester) async {
tester.binding.addTime(const Duration(seconds: 3));
......
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