fix(material/a11y_assessments): add unique page title (#152148)

Adds usage of SystemChrome.setAPplicationSwitcherDescription to update the page title for the AutoComplete demo page.

[Before screenshot: example AutoComplete](https://screenshot.googleplex.com/8DpESj4QrQA2YrW)
[After screenshot: example AutoComplete](https://screenshot.googleplex.com/6AdfSA8a8VwUaXM)

Fixes b/338056490

*Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.*

*List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.*

*If you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].*
This commit is contained in:
Joy Serquiña 2024-08-22 14:28:22 -07:00 committed by GitHub
parent 8e50a17d94
commit 5fad2bea2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 350 additions and 65 deletions

View File

@ -0,0 +1,21 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// 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';
class DynamicTitle extends StatelessWidget {
const DynamicTitle({super.key, required this.title, required this.child});
final String title;
final Widget child;
@override
Widget build(BuildContext context) {
return Title(
title: title,
color: Theme.of(context).colorScheme.primary,
child: child,
);
}
}

View File

@ -35,10 +35,11 @@ class App extends StatelessWidget {
final Map<String, WidgetBuilder> routes = final Map<String, WidgetBuilder> routes =
Map<String, WidgetBuilder>.fromEntries( Map<String, WidgetBuilder>.fromEntries(
useCases.map((UseCase useCase) => useCases.map((UseCase useCase) =>
MapEntry<String, WidgetBuilder>(useCase.route, useCase.build)), MapEntry<String, WidgetBuilder>(useCase.route, (BuildContext context) => useCase.buildWithTitle(context))),
); );
return MaterialApp( return MaterialApp(
title: 'Accessibility Assessments', title: 'Accessibility Assessments Home Page',
theme: lightTheme, theme: lightTheme,
darkTheme: darkTheme, darkTheme: darkTheme,
routes: <String, WidgetBuilder>{'/': (_) => const HomePage(), ...routes}, routes: <String, WidgetBuilder>{'/': (_) => const HomePage(), ...routes},
@ -68,7 +69,7 @@ class HomePageState extends State<HomePage> {
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
return TextButton( return TextButton(
key: Key(useCase.name), key: Key(useCase.name),
onPressed: () => Navigator.of(context).pushNamed(useCase.route), onPressed: () => Navigator.of(context).pushNamed(useCase.route, arguments: useCase.name),
child: Text(useCase.name), child: Text(useCase.name),
); );
})); }));
@ -76,9 +77,10 @@ class HomePageState extends State<HomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('Accessibility Assessments')), title: Semantics(headingLevel: 1, child: const Text('Accessibility Assessments')),
), ),
body: Center( body: Center(
child: ListView( child: ListView(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class ActionChipUseCase extends UseCase { class ActionChipUseCase extends UseCase {
@ -26,12 +26,15 @@ class MainWidget extends StatefulWidget {
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
bool favorite = false; bool favorite = false;
String pageTitle = getUseCaseName(ActionChipUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel:1, child: const Text('ActionChip')), title: Semantics(headingLevel:1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: Column( child: Column(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class AutoCompleteUseCase extends UseCase { class AutoCompleteUseCase extends UseCase {
@ -45,12 +45,14 @@ class _MainWidgetState extends State<_MainWidget> {
); );
} }
String pageTitle = getUseCaseName(AutoCompleteUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('AutoComplete Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: Column( child: Column(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class BadgeUseCase extends UseCase { class BadgeUseCase extends UseCase {
@ -25,13 +25,14 @@ class MainWidget extends StatefulWidget {
} }
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
String pageTitle = getUseCaseName(BadgeUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Badge Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: const Center( body: const Center(
child: Badge( child: Badge(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class CardUseCase extends UseCase { class CardUseCase extends UseCase {
@ -26,12 +26,15 @@ class MainWidget extends StatefulWidget {
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
bool favorite = false; bool favorite = false;
String pageTitle = getUseCaseName(CardUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Card')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: const Center( body: const Center(
child: Column( child: Column(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class CheckBoxListTile extends UseCase { class CheckBoxListTile extends UseCase {
@ -25,11 +25,13 @@ class _MainWidget extends StatefulWidget {
class _MainWidgetState extends State<_MainWidget> { class _MainWidgetState extends State<_MainWidget> {
bool _checked = false; bool _checked = false;
String pageTitle = getUseCaseName(CheckBoxListTile());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('CheckBoxListTile Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: ListView( body: ListView(
children: <Widget>[ children: <Widget>[

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class DatePickerUseCase extends UseCase { class DatePickerUseCase extends UseCase {
@ -25,12 +25,15 @@ class _MainWidget extends StatefulWidget {
} }
class _MainWidgetState extends State<_MainWidget> { class _MainWidgetState extends State<_MainWidget> {
String pageTitle = getUseCaseName(DatePickerUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('DatePicker Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: TextButton( child: TextButton(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class DialogUseCase extends UseCase { class DialogUseCase extends UseCase {
@ -14,17 +14,20 @@ class DialogUseCase extends UseCase {
String get route => '/dialog'; String get route => '/dialog';
@override @override
Widget build(BuildContext context) => const _MainWidget(); Widget build(BuildContext context) => _MainWidget();
} }
class _MainWidget extends StatelessWidget { class _MainWidget extends StatelessWidget {
const _MainWidget(); _MainWidget();
final String pageTitle = getUseCaseName(DialogUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Dialog Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: TextButton( child: TextButton(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class DrawerUseCase extends UseCase { class DrawerUseCase extends UseCase {
@ -27,12 +27,14 @@ class DrawerExample extends StatefulWidget {
class _DrawerExampleState extends State<DrawerExample> { class _DrawerExampleState extends State<DrawerExample> {
String selectedPage = ''; String selectedPage = '';
String pageTitle = getUseCaseName(DrawerUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Drawer Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
endDrawer: Drawer( endDrawer: Drawer(
child: ListView( child: ListView(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class ExpansionTileUseCase extends UseCase { class ExpansionTileUseCase extends UseCase {
@ -27,12 +27,14 @@ class ExpansionTileExample extends StatefulWidget {
class _ExpansionTileExampleState extends State<ExpansionTileExample> { class _ExpansionTileExampleState extends State<ExpansionTileExample> {
bool _customTileExpanded = false; bool _customTileExpanded = false;
String pageTitle = getUseCaseName(ExpansionTileUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('ExpansionTile')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
@ -47,9 +49,7 @@ class _ExpansionTileExampleState extends State<ExpansionTileExample> {
title: const Text('ExpansionTile 2'), title: const Text('ExpansionTile 2'),
subtitle: const Text('Custom expansion arrow icon'), subtitle: const Text('Custom expansion arrow icon'),
trailing: Icon( trailing: Icon(
_customTileExpanded _customTileExpanded ? Icons.arrow_drop_down_circle : Icons.arrow_drop_down,
? Icons.arrow_drop_down_circle
: Icons.arrow_drop_down,
), ),
children: const <Widget>[ children: const <Widget>[
ListTile(title: Text('This is tile number 2')), ListTile(title: Text('This is tile number 2')),

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class MaterialBannerUseCase extends UseCase { class MaterialBannerUseCase extends UseCase {
@ -29,6 +29,8 @@ class MainWidgetState extends State<MainWidget> {
final FocusNode dismissButtonFocusNode = FocusNode(); final FocusNode dismissButtonFocusNode = FocusNode();
final FocusNode showButtonFocusNode = FocusNode(); final FocusNode showButtonFocusNode = FocusNode();
String pageTitle = getUseCaseName(MaterialBannerUseCase());
@override @override
void dispose() { void dispose() {
dismissButtonFocusNode.dispose(); dismissButtonFocusNode.dispose();
@ -62,10 +64,10 @@ class MainWidgetState extends State<MainWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('MaterialBanner Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: ElevatedButton( child: ElevatedButton(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class NavigationBarUseCase extends UseCase { class NavigationBarUseCase extends UseCase {
@ -27,12 +27,14 @@ class MainWidget extends StatefulWidget {
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
int currentPageIndex = 0; int currentPageIndex = 0;
String pageTitle = getUseCaseName(NavigationBarUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('NavigationBar Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
bottomNavigationBar: NavigationBar( bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) { onDestinationSelected: (int index) {

View File

@ -2,10 +2,12 @@
// 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 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class ExampleDestination { class ExampleDestination {
const ExampleDestination(this.label, this.icon, this.selectedIcon); const ExampleDestination(this.label, this.icon, this.selectedIcon);
@ -58,12 +60,14 @@ class _NavigationDrawerExampleState extends State<NavigationDrawerExample> {
scaffoldKey.currentState!.openEndDrawer(); scaffoldKey.currentState!.openEndDrawer();
} }
String pageTitle = getUseCaseName(NavigationDrawerUseCase());
Widget buildDrawerScaffold(BuildContext context) { Widget buildDrawerScaffold(BuildContext context) {
return Scaffold( return Scaffold(
key: scaffoldKey, key: scaffoldKey,
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Navigation Drawer Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: SafeArea( body: SafeArea(
bottom: false, bottom: false,

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class NavigationRailUseCase extends UseCase { class NavigationRailUseCase extends UseCase {
@ -31,6 +31,8 @@ class _NavRailExampleState extends State<NavRailExample> {
bool showTrailing = false; bool showTrailing = false;
double groupAlignment = -1.0; double groupAlignment = -1.0;
String pageTitle = getUseCaseName(NavigationRailUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class RadioListTileUseCase extends UseCase { class RadioListTileUseCase extends UseCase {
@ -33,11 +33,13 @@ class _MainWidgetState extends State<_MainWidget> {
}); });
} }
String pageTitle = getUseCaseName(RadioListTileUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Semantics(headingLevel: 1, child: const Text('Radio button demo')) title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo'))
), ),
body: ListView( body: ListView(
children: <Widget>[ children: <Widget>[

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class SliderUseCase extends UseCase { class SliderUseCase extends UseCase {
@ -28,12 +28,14 @@ class MainWidgetState extends State<MainWidget> {
double currentSliderValue = 20; double currentSliderValue = 20;
static const String accessibilityLabel = 'Accessibility Test Slider'; static const String accessibilityLabel = 'Accessibility Test Slider';
String pageTitle = getUseCaseName(SliderUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('Slider demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle demo')),
), ),
body: Center( body: Center(
child: Semantics( child: Semantics(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class SnackBarUseCase extends UseCase { class SnackBarUseCase extends UseCase {
@ -25,12 +25,15 @@ class MainWidget extends StatefulWidget {
} }
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
String pageTitle = getUseCaseName(SnackBarUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('SnackBar')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: Column( child: Column(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class SwitchListTileUseCase extends UseCase { class SwitchListTileUseCase extends UseCase {
@ -27,12 +27,15 @@ class SwitchListTileExample extends StatefulWidget {
class _SwitchListTileExampleState extends State<SwitchListTileExample> { class _SwitchListTileExampleState extends State<SwitchListTileExample> {
bool _lights1 = false; bool _lights1 = false;
bool _lights2 = false; bool _lights2 = false;
String pageTitle = getUseCaseName(SwitchListTileUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('SwitchListTile')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: Column( child: Column(

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class TextButtonUseCase extends UseCase { class TextButtonUseCase extends UseCase {
@ -27,12 +27,14 @@ class MainWidget extends StatefulWidget {
class MainWidgetState extends State<MainWidget> { class MainWidgetState extends State<MainWidget> {
int _count = 0; int _count = 0;
String pageTitle = getUseCaseName(TextButtonUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('TextButton Demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: Center( body: Center(
child: Column( child: Column(
@ -45,9 +47,7 @@ class MainWidgetState extends State<MainWidget> {
const Text('This is a TextButton:'), const Text('This is a TextButton:'),
TextButton( TextButton(
onPressed: () { onPressed: () {
setState(() { setState(() { _count++; });
_count++;
});
}, },
child: const Text('Action'), child: const Text('Action'),
), ),

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class TextFieldUseCase extends UseCase { class TextFieldUseCase extends UseCase {
@ -14,18 +14,20 @@ class TextFieldUseCase extends UseCase {
String get route => '/text-field'; String get route => '/text-field';
@override @override
Widget build(BuildContext context) => const _MainWidget(); Widget build(BuildContext context) => _MainWidget();
} }
class _MainWidget extends StatelessWidget { class _MainWidget extends StatelessWidget {
const _MainWidget(); _MainWidget();
final String pageTitle = getUseCaseName(TextFieldUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('TextField demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: ListView( body: ListView(
children: <Widget>[ children: <Widget>[

View File

@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils.dart';
import 'use_cases.dart'; import 'use_cases.dart';
class TextFieldPasswordUseCase extends UseCase { class TextFieldPasswordUseCase extends UseCase {
@ -14,18 +14,20 @@ class TextFieldPasswordUseCase extends UseCase {
String get route => '/text-field-password'; String get route => '/text-field-password';
@override @override
Widget build(BuildContext context) => const _MainWidget(); Widget build(BuildContext context) => _MainWidget();
} }
class _MainWidget extends StatelessWidget { class _MainWidget extends StatelessWidget {
const _MainWidget(); _MainWidget();
final String pageTitle = getUseCaseName(TextFieldPasswordUseCase());
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary, backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Semantics(headingLevel: 1, child: const Text('TextField password demo')), title: Semantics(headingLevel: 1, child: Text('$pageTitle Demo')),
), ),
body: ListView( body: ListView(
children: const <Widget>[ children: const <Widget>[

View File

@ -4,6 +4,7 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import '../common/dynamic_title.dart';
import 'action_chip.dart'; import 'action_chip.dart';
import 'auto_complete.dart'; import 'auto_complete.dart';
import 'badge.dart'; import 'badge.dart';
@ -28,6 +29,14 @@ import 'text_field_password.dart';
abstract class UseCase { abstract class UseCase {
String get name; String get name;
String get route; String get route;
Widget buildWithTitle(BuildContext context) {
return DynamicTitle(
title: name,
child: build(context),
);
}
Widget build(BuildContext context); Widget build(BuildContext context);
} }

View File

@ -0,0 +1,7 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import './use_cases/use_cases.dart';
String getUseCaseName(UseCase useCase) => useCase.name;

View File

@ -13,4 +13,11 @@ void main() {
await pumpsUseCase(tester, ActionChipUseCase()); await pumpsUseCase(tester, ActionChipUseCase());
expect(find.byType(ActionChip), findsExactly(2)); expect(find.byType(ActionChip), findsExactly(2));
}); });
testWidgets('action chip has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, ActionChipUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel(RegExp('ActionChip Demo'));
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
} }

View File

@ -13,4 +13,11 @@ void main() {
await pumpsUseCase(tester, CardUseCase()); await pumpsUseCase(tester, CardUseCase());
expect(find.byType(Card), findsExactly(1)); expect(find.byType(Card), findsExactly(1));
}); });
testWidgets('card has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, CardUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel(RegExp('Card Demo'));
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
} }

View File

@ -23,7 +23,7 @@ void main() {
testWidgets('drawer has one h1 tag', (WidgetTester tester) async { testWidgets('drawer has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, DrawerUseCase()); await pumpsUseCase(tester, DrawerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Drawer Demo'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('drawer Demo');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne); expect(findHeadingLevelOnes, findsOne);
}); });

View File

@ -9,8 +9,15 @@ import 'package:flutter_test/flutter_test.dart';
import 'test_utils.dart'; import 'test_utils.dart';
void main() { void main() {
testWidgets('action chip can run', (WidgetTester tester) async { testWidgets('expansion tile can run', (WidgetTester tester) async {
await pumpsUseCase(tester, ExpansionTileUseCase()); await pumpsUseCase(tester, ExpansionTileUseCase());
expect(find.byType(ExpansionTile), findsExactly(3)); expect(find.byType(ExpansionTile), findsExactly(3));
}); });
testWidgets('exapansion tile has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, ExpansionTileUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('ExpansionTile Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
} }

View File

@ -2,11 +2,36 @@
// 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.
// ignore_for_file: avoid_dynamic_calls
import 'package:a11y_assessments/main.dart'; import 'package:a11y_assessments/main.dart';
import 'package:a11y_assessments/use_cases/action_chip.dart';
import 'package:a11y_assessments/use_cases/auto_complete.dart';
import 'package:a11y_assessments/use_cases/badge.dart';
import 'package:a11y_assessments/use_cases/card.dart';
import 'package:a11y_assessments/use_cases/check_box_list_tile.dart';
import 'package:a11y_assessments/use_cases/date_picker.dart';
import 'package:a11y_assessments/use_cases/dialog.dart';
import 'package:a11y_assessments/use_cases/drawer.dart';
import 'package:a11y_assessments/use_cases/expansion_tile.dart';
import 'package:a11y_assessments/use_cases/material_banner.dart';
import 'package:a11y_assessments/use_cases/navigation_bar.dart';
import 'package:a11y_assessments/use_cases/navigation_drawer.dart';
import 'package:a11y_assessments/use_cases/navigation_rail.dart';
import 'package:a11y_assessments/use_cases/radio_list_tile.dart';
import 'package:a11y_assessments/use_cases/slider.dart';
import 'package:a11y_assessments/use_cases/snack_bar.dart';
import 'package:a11y_assessments/use_cases/switch_list_tile.dart';
import 'package:a11y_assessments/use_cases/text_button.dart';
import 'package:a11y_assessments/use_cases/text_field.dart';
import 'package:a11y_assessments/use_cases/text_field_password.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:material_color_utilities/material_color_utilities.dart'; import 'package:material_color_utilities/material_color_utilities.dart';
import 'test_utils.dart';
void main() { void main() {
testWidgets('Has light and dark theme', (WidgetTester tester) async { testWidgets('Has light and dark theme', (WidgetTester tester) async {
await tester.pumpWidget(const App()); await tester.pumpWidget(const App());
@ -141,6 +166,149 @@ void main() {
MaterialDynamicColors.inversePrimary.getArgb(highContrastScheme)); MaterialDynamicColors.inversePrimary.getArgb(highContrastScheme));
}); });
testWidgets('Each A11y Assessments page has a unique page title.', (WidgetTester tester) async {
final List<MethodCall> log = <MethodCall>[];
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
if (methodCall.method == 'SystemChrome.setApplicationSwitcherDescription') {
log.add(methodCall);
}
return null;
});
await tester.pumpWidget(Title(
color: const Color(0xFF00FF00),
title: 'Accessibility Assessments',
child: Container(),
));
expect(log[0], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'Accessibility Assessments', 'primaryColor': 4278255360},
));
await pumpsUseCase(tester, AutoCompleteUseCase());
expect(log[2], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'AutoComplete', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, ActionChipUseCase());
expect(log[4], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'ActionChip', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, BadgeUseCase());
expect(log[6], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'Badge', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, CardUseCase());
expect(log[8], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'Card', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, CheckBoxListTile());
expect(log[10], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'CheckBoxListTile', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, DatePickerUseCase());
expect(log[12], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'DatePicker', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, DialogUseCase());
expect(log[14], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'Dialog', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, ExpansionTileUseCase());
expect(log[16], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'ExpansionTile', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, MaterialBannerUseCase());
expect(log[18], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'MaterialBanner', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, NavigationBarUseCase());
expect(log[20], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'NavigationBar', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, RadioListTileUseCase());
expect(log[22], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'RadioListTile', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, SliderUseCase());
expect(log[24], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'Slider', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, SnackBarUseCase());
expect(log[26], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'SnackBar', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, SwitchListTileUseCase());
expect(log[28], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'SwitchListTile', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, TextButtonUseCase());
expect(log[30], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'TextButton', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, TextFieldUseCase());
expect(log[32], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'TextField', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, TextFieldPasswordUseCase());
expect(log[34], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'TextField password', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, NavigationDrawerUseCase());
expect(log[36], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'NavigationDrawer', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, NavigationRailUseCase());
expect(log[38], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'NavigationRail', 'primaryColor': 4284960932},
));
await pumpsUseCase(tester, DrawerUseCase());
expect(log[40], isMethodCall(
'SystemChrome.setApplicationSwitcherDescription',
arguments: <String, dynamic>{'label': 'drawer', 'primaryColor': 4284960932},
));
});
testWidgets('a11y assessments home page has one h1 tag', (WidgetTester tester) async { testWidgets('a11y assessments home page has one h1 tag', (WidgetTester tester) async {
await tester.pumpWidget(const App()); await tester.pumpWidget(const App());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Accessibility Assessments'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('Accessibility Assessments');

View File

@ -23,7 +23,7 @@ void main() {
testWidgets('navigation drawer has one h1 tag', (WidgetTester tester) async { testWidgets('navigation drawer has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, NavigationDrawerUseCase()); await pumpsUseCase(tester, NavigationDrawerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Navigation Drawer Demo'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('NavigationDrawer Demo');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne); expect(findHeadingLevelOnes, findsOne);
}); });

View File

@ -15,9 +15,9 @@ void main() {
expect(find.text('Jefferson'), findsOneWidget); expect(find.text('Jefferson'), findsOneWidget);
}); });
testWidgets('radio button demo page has one h1 tag', (WidgetTester tester) async { testWidgets('radio list tile demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, RadioListTileUseCase()); await pumpsUseCase(tester, RadioListTileUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('Radio button demo'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('RadioListTile Demo');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne); expect(findHeadingLevelOnes, findsOne);
}); });

View File

@ -17,4 +17,11 @@ void main() {
await tester.pump(); await tester.pump();
expect(find.text(snackBarText), findsOneWidget); expect(find.text(snackBarText), findsOneWidget);
}); });
testWidgets('snack bar demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, SnackBarUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('SnackBar Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
} }

View File

@ -9,8 +9,15 @@ import 'package:flutter_test/flutter_test.dart';
import 'test_utils.dart'; import 'test_utils.dart';
void main() { void main() {
testWidgets('action chip can run', (WidgetTester tester) async { testWidgets('switch list can run', (WidgetTester tester) async {
await pumpsUseCase(tester, SwitchListTileUseCase()); await pumpsUseCase(tester, SwitchListTileUseCase());
expect(find.byType(SwitchListTile), findsExactly(2)); expect(find.byType(SwitchListTile), findsExactly(2));
}); });
testWidgets('switch list demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, SwitchListTileUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('SwitchListTile Demo');
await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne);
});
} }

View File

@ -10,7 +10,7 @@ Future<void> pumpsUseCase(WidgetTester tester, UseCase useCase) async {
await tester.pumpWidget(MaterialApp( await tester.pumpWidget(MaterialApp(
home: Builder( home: Builder(
builder: (BuildContext context) { builder: (BuildContext context) {
return useCase.build(context); return useCase.buildWithTitle(context);
}, },
), ),
)); ));

View File

@ -53,7 +53,7 @@ void main() {
testWidgets('text field password demo page has one h1 tag', (WidgetTester tester) async { testWidgets('text field password demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, TextFieldPasswordUseCase()); await pumpsUseCase(tester, TextFieldPasswordUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField password demo'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField password Demo');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne); expect(findHeadingLevelOnes, findsOne);
}); });

View File

@ -67,7 +67,7 @@ void main() {
testWidgets('text field demo page has one h1 tag', (WidgetTester tester) async { testWidgets('text field demo page has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, TextFieldUseCase()); await pumpsUseCase(tester, TextFieldUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField demo'); final Finder findHeadingLevelOnes = find.bySemanticsLabel('TextField Demo');
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(findHeadingLevelOnes, findsOne); expect(findHeadingLevelOnes, findsOne);
}); });