Skip to content

Commit ee63060

Browse files
authored
[A11y] Add UI for AccessibilityOverridesPane (#9892)
* AccessibilityOverridesPane UI * 1 * resolve comments * Update overrides_pane.dart * small fix * resolve comments * lint
1 parent 90487c0 commit ee63060

5 files changed

Lines changed: 357 additions & 36 deletions

File tree

packages/devtools_app/lib/src/screens/accessibility/accessibility_controller.dart

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ import 'package:flutter/foundation.dart';
88
import '../../shared/framework/screen.dart';
99
import '../../shared/framework/screen_controllers.dart';
1010

11+
/// Modes for brightness override in the accessibility controls.
12+
enum BrightnessOverride {
13+
system('System Default'),
14+
light('Light Mode'),
15+
dark('Dark Mode');
16+
17+
const BrightnessOverride(this.display);
18+
19+
final String display;
20+
}
21+
1122
/// Controller for the Accessibility screen.
1223
class AccessibilityController extends DevToolsScreenController
1324
with AutoDisposeControllerMixin {
@@ -49,7 +60,9 @@ class AccessibilityController extends DevToolsScreenController
4960
final screenId = ScreenMetaData.accessibility.id;
5061

5162
// --- Accessibility Overrides State ---
52-
final brightness = ValueNotifier<String>('System');
63+
final brightness = ValueNotifier<BrightnessOverride>(
64+
BrightnessOverride.system,
65+
);
5366
final textScale = ValueNotifier<double>(1.0);
5467
final boldText = ValueNotifier<bool>(false);
5568
final screenReader = ValueNotifier<bool>(false);

packages/devtools_app/lib/src/screens/accessibility/accessibility_screen.dart

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@ import 'package:flutter/material.dart';
88

99
import '../../shared/framework/screen.dart';
1010
import '../../shared/globals.dart';
11-
import '../../shared/ui/common_widgets.dart';
1211
import 'accessibility_controller.dart';
12+
import 'overrides_pane.dart';
13+
import 'semantics_tree_pane.dart';
14+
15+
export 'overrides_pane.dart';
16+
export 'semantics_tree_pane.dart';
1317

1418
/// A screen that displays accessibility information.
1519
class AccessibilityScreen extends Screen {
@@ -58,37 +62,3 @@ class _AccessibilityScreenBodyState extends State<AccessibilityScreenBody>
5862
return screenSize.width > 1000 ? Axis.horizontal : Axis.vertical;
5963
}
6064
}
61-
62-
/// A pane that displays the semantics tree of the connected app.
63-
class AccessibilitySemanticsTreePane extends StatelessWidget {
64-
const AccessibilitySemanticsTreePane({super.key});
65-
66-
@override
67-
Widget build(BuildContext context) {
68-
return const DevToolsAreaPane(
69-
header: AreaPaneHeader(title: Text('Semantics Tree')),
70-
child: CenteredMessage(
71-
message:
72-
'Accessibility semantics tree placeholder.\n'
73-
'// TODO(hannah-hyj): Implement semantics tree view and details explorer.',
74-
),
75-
);
76-
}
77-
}
78-
79-
/// A pane that displays the accessibility overrides controls.
80-
class AccessibilityOverridesPane extends StatelessWidget {
81-
const AccessibilityOverridesPane({super.key});
82-
83-
@override
84-
Widget build(BuildContext context) {
85-
return const DevToolsAreaPane(
86-
header: AreaPaneHeader(title: Text('Accessibility Overrides')),
87-
child: CenteredMessage(
88-
message:
89-
'Accessibility overrides placeholder.\n'
90-
'// TODO(hannah-hyj): Implement setting overrides (brightness, text scale, bold text, screen reader, high contrast).',
91-
),
92-
);
93-
}
94-
}
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// Copyright 2026 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
4+
5+
import 'package:devtools_app_shared/ui.dart';
6+
import 'package:flutter/material.dart';
7+
8+
import '../../shared/globals.dart';
9+
import '../../shared/ui/common_widgets.dart';
10+
import 'accessibility_controller.dart';
11+
12+
/// A pane that displays the accessibility overrides controls.
13+
class AccessibilityOverridesPane extends StatelessWidget {
14+
const AccessibilityOverridesPane({super.key});
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
final theme = Theme.of(context);
19+
final controller = screenControllers.lookup<AccessibilityController>();
20+
return DevToolsAreaPane(
21+
header: const AreaPaneHeader(
22+
title: Text('Accessibility Overrides'),
23+
roundedTopBorder: false,
24+
includeTopBorder: false,
25+
),
26+
child: Scrollbar(
27+
child: SingleChildScrollView(
28+
padding: const EdgeInsets.all(defaultSpacing),
29+
child: Column(
30+
crossAxisAlignment: CrossAxisAlignment.start,
31+
children: [
32+
Text(
33+
'Simulate and test accessibility settings on the connected device in real-time.',
34+
style: theme.subtleTextStyle,
35+
),
36+
const SizedBox(height: defaultSpacing),
37+
const Divider(),
38+
const SizedBox(height: denseSpacing),
39+
_BrightnessOverride(controller: controller),
40+
const SizedBox(height: defaultSpacing),
41+
_TextScaleOverride(controller: controller),
42+
const SizedBox(height: defaultSpacing),
43+
_SwitchOverride(
44+
label: 'Bold Text',
45+
description: 'Forces all text in the application to be bold.',
46+
notifier: controller.boldText,
47+
),
48+
const SizedBox(height: defaultSpacing),
49+
_SwitchOverride(
50+
label: 'Screen Reader Debugger',
51+
description: 'Debug and test screen reader layouts.',
52+
notifier: controller.screenReader,
53+
),
54+
const SizedBox(height: defaultSpacing),
55+
_SwitchOverride(
56+
label: 'High Contrast',
57+
description: 'Increases the contrast of text and icons.',
58+
notifier: controller.highContrast,
59+
),
60+
],
61+
),
62+
),
63+
),
64+
);
65+
}
66+
}
67+
68+
class _AccessibilityPanelLabel extends StatelessWidget {
69+
const _AccessibilityPanelLabel({
70+
required this.label,
71+
required this.description,
72+
});
73+
74+
final String label;
75+
final String description;
76+
77+
@override
78+
Widget build(BuildContext context) {
79+
final theme = Theme.of(context);
80+
return Column(
81+
crossAxisAlignment: CrossAxisAlignment.start,
82+
children: [
83+
Text(label, style: theme.boldTextStyle),
84+
const SizedBox(height: densePadding),
85+
Text(description, style: theme.subtleTextStyle),
86+
],
87+
);
88+
}
89+
}
90+
91+
class _BrightnessOverride extends StatelessWidget {
92+
const _BrightnessOverride({required this.controller});
93+
94+
final AccessibilityController controller;
95+
96+
@override
97+
Widget build(BuildContext context) {
98+
return Column(
99+
crossAxisAlignment: CrossAxisAlignment.start,
100+
children: [
101+
const _AccessibilityPanelLabel(
102+
label: 'Brightness',
103+
description: 'Override the color scheme mode of the app.',
104+
),
105+
const SizedBox(height: denseSpacing),
106+
ValueListenableBuilder<BrightnessOverride>(
107+
valueListenable: controller.brightness,
108+
builder: (context, value, _) {
109+
return RoundedDropDownButton<BrightnessOverride>(
110+
isExpanded: true,
111+
value: value,
112+
items: BrightnessOverride.values.map((option) {
113+
return DropdownMenuItem<BrightnessOverride>(
114+
value: option,
115+
child: Text(option.display),
116+
);
117+
}).toList(),
118+
onChanged: (newValue) {
119+
if (newValue != null) {
120+
controller.brightness.value = newValue;
121+
}
122+
},
123+
);
124+
},
125+
),
126+
],
127+
);
128+
}
129+
}
130+
131+
class _TextScaleOverride extends StatelessWidget {
132+
const _TextScaleOverride({required this.controller});
133+
134+
final AccessibilityController controller;
135+
136+
static const _minTextScale = 0.5;
137+
static const _maxTextScale = 3.0;
138+
static const _textScaleDivisions = 25;
139+
140+
@override
141+
Widget build(BuildContext context) {
142+
final theme = Theme.of(context);
143+
return ValueListenableBuilder<double>(
144+
valueListenable: controller.textScale,
145+
builder: (context, value, _) {
146+
return Column(
147+
crossAxisAlignment: CrossAxisAlignment.start,
148+
children: [
149+
Row(
150+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
151+
children: [
152+
const _AccessibilityPanelLabel(
153+
label: 'Text Scale',
154+
description: 'Scale the system font size.',
155+
),
156+
Text(
157+
'${value.toStringAsFixed(2)}x',
158+
style: theme.boldTextStyle,
159+
),
160+
],
161+
),
162+
const SizedBox(height: densePadding),
163+
Slider(
164+
value: value,
165+
min: _minTextScale,
166+
max: _maxTextScale,
167+
divisions: _textScaleDivisions,
168+
onChanged: (newValue) {
169+
controller.textScale.value = newValue;
170+
},
171+
),
172+
],
173+
);
174+
},
175+
);
176+
}
177+
}
178+
179+
class _SwitchOverride extends StatelessWidget {
180+
const _SwitchOverride({
181+
required this.label,
182+
required this.description,
183+
required this.notifier,
184+
});
185+
186+
final String label;
187+
final String description;
188+
final ValueNotifier<bool> notifier;
189+
190+
@override
191+
Widget build(BuildContext context) {
192+
return Row(
193+
children: [
194+
Expanded(
195+
child: _AccessibilityPanelLabel(
196+
label: label,
197+
description: description,
198+
),
199+
),
200+
NotifierSwitch(notifier: notifier),
201+
],
202+
);
203+
}
204+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2026 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
4+
5+
import 'package:devtools_app_shared/ui.dart';
6+
import 'package:flutter/material.dart';
7+
8+
import '../../shared/ui/common_widgets.dart';
9+
10+
/// A pane that displays the semantics tree of the connected app.
11+
class AccessibilitySemanticsTreePane extends StatelessWidget {
12+
const AccessibilitySemanticsTreePane({super.key});
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
return const DevToolsAreaPane(
17+
header: AreaPaneHeader(
18+
title: Text('Semantics Tree'),
19+
includeTopBorder: false,
20+
roundedTopBorder: false,
21+
),
22+
child: CenteredMessage(
23+
message:
24+
'Accessibility semantics tree placeholder.\n'
25+
'// TODO(hannah-hyj): Implement semantics tree view and details explorer.',
26+
),
27+
);
28+
}
29+
}

0 commit comments

Comments
 (0)