-
Notifications
You must be signed in to change notification settings - Fork 399
[A11y] Add UI for AccessibilityOverridesPane #9892
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
349a778
562125b
7c9ade1
64e52ef
58e31e1
f5f931d
fe091fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,13 +82,211 @@ class AccessibilityOverridesPane extends StatelessWidget { | |
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return const DevToolsAreaPane( | ||
| header: AreaPaneHeader(title: Text('Accessibility Overrides')), | ||
| child: CenteredMessage( | ||
| message: | ||
| 'Accessibility overrides placeholder.\n' | ||
| '// TODO(hannah-hyj): Implement setting overrides (brightness, text scale, bold text, screen reader, high contrast).', | ||
| final theme = Theme.of(context); | ||
| final controller = screenControllers.lookup<AccessibilityController>(); | ||
| return DevToolsAreaPane( | ||
| header: const AreaPaneHeader(title: Text('Accessibility Overrides')), | ||
| child: Scrollbar( | ||
| child: SingleChildScrollView( | ||
| padding: const EdgeInsets.all(defaultSpacing), | ||
| child: Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| Text( | ||
| 'Simulate and test accessibility settings on the connected device in real-time.', | ||
| style: theme.subtleTextStyle, | ||
| ), | ||
| const SizedBox(height: defaultSpacing), | ||
| const Divider(), | ||
| const SizedBox(height: denseSpacing), | ||
| _BrightnessOverride(controller: controller), | ||
| const SizedBox(height: defaultSpacing), | ||
| _TextScaleOverride(controller: controller), | ||
| const SizedBox(height: defaultSpacing), | ||
| _SwitchOverride( | ||
| label: 'Bold Text', | ||
| description: 'Forces all text in the application to be bold.', | ||
| notifier: controller.boldText, | ||
| ), | ||
| const SizedBox(height: defaultSpacing), | ||
| _SwitchOverride( | ||
| label: 'Screen Reader Debugger', | ||
| description: | ||
| 'Enables interactive screen reader simulation semantics.', | ||
| notifier: controller.screenReader, | ||
| ), | ||
| const SizedBox(height: defaultSpacing), | ||
| _SwitchOverride( | ||
| label: 'High Contrast', | ||
| description: 'Increases the contrast of text and icons.', | ||
| notifier: controller.highContrast, | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| class _BrightnessOverride extends StatelessWidget { | ||
| const _BrightnessOverride({required this.controller}); | ||
|
|
||
| final AccessibilityController controller; | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| final theme = Theme.of(context); | ||
| return Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| Text( | ||
| 'Brightness', | ||
| style: theme.boldTextStyle, | ||
| ), | ||
| const SizedBox(height: densePadding), | ||
| Text( | ||
| 'Override the color scheme mode of the app.', | ||
| style: theme.subtleTextStyle, | ||
| ), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider defining a widget (e.g. |
||
| const SizedBox(height: denseSpacing), | ||
| ValueListenableBuilder<String>( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since there are set number of options here, I think it might be better for |
||
| valueListenable: controller.brightness, | ||
| builder: (context, value, _) { | ||
| return RoundedDropDownButton<String>( | ||
| isExpanded: true, | ||
| value: value, | ||
| items: const [ | ||
| DropdownMenuItem( | ||
| value: 'System', | ||
| child: Text('System Default'), | ||
| ), | ||
| DropdownMenuItem( | ||
| value: 'Light', | ||
| child: Text('Light Mode'), | ||
| ), | ||
| DropdownMenuItem( | ||
| value: 'Dark', | ||
| child: Text('Dark Mode'), | ||
| ), | ||
| ], | ||
| onChanged: (newValue) { | ||
| if (newValue != null) { | ||
| controller.brightness.value = newValue; | ||
| } | ||
| }, | ||
| ); | ||
| }, | ||
| ), | ||
| ], | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| class _TextScaleOverride extends StatelessWidget { | ||
| const _TextScaleOverride({required this.controller}); | ||
|
|
||
| final AccessibilityController controller; | ||
|
|
||
| static const _minTextScale = 0.5; | ||
| static const _maxTextScale = 3.0; | ||
| static const _textScaleDivisions = 25; | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| final theme = Theme.of(context); | ||
| return ValueListenableBuilder<double>( | ||
| valueListenable: controller.textScale, | ||
| builder: (context, value, _) { | ||
| return Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| children: [ | ||
| Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| Text( | ||
| 'Text Scale', | ||
| style: theme.boldTextStyle, | ||
| ), | ||
| const SizedBox(height: densePadding), | ||
| Text( | ||
| 'Scale the system font size.', | ||
| style: theme.subtleTextStyle, | ||
| ), | ||
| ], | ||
| ), | ||
| Text( | ||
| '${value.toStringAsFixed(2)}x', | ||
| style: theme.boldTextStyle, | ||
| ), | ||
| ], | ||
| ), | ||
| const SizedBox(height: densePadding), | ||
| Slider( | ||
| value: value, | ||
| min: _minTextScale, | ||
| max: _maxTextScale, | ||
| divisions: _textScaleDivisions, | ||
| onChanged: (newValue) { | ||
| controller.textScale.value = newValue; | ||
| }, | ||
| ), | ||
| ], | ||
| ); | ||
| }, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| class _SwitchOverride extends StatelessWidget { | ||
| const _SwitchOverride({ | ||
| required this.label, | ||
| required this.description, | ||
| required this.notifier, | ||
| }); | ||
|
|
||
| final String label; | ||
| final String description; | ||
| final ValueNotifier<bool> notifier; | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| final theme = Theme.of(context); | ||
| return Row( | ||
| children: [ | ||
| Expanded( | ||
| child: Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| Text( | ||
| label, | ||
| style: theme.boldTextStyle, | ||
| ), | ||
| const SizedBox(height: densePadding), | ||
| Text( | ||
| description, | ||
| style: theme.subtleTextStyle, | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
| ValueListenableBuilder<bool>( | ||
| valueListenable: notifier, | ||
| builder: (context, enabled, _) { | ||
| return Switch( | ||
| value: enabled, | ||
| onChanged: (newValue) { | ||
| notifier.value = newValue; | ||
| }, | ||
| ); | ||
| }, | ||
| ), | ||
| ], | ||
| ); | ||
| } | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on screenshot, it looks like this is causing the border to be rendered twice. You likely need to pass
includeTopBorder: false(ditto for the semantics tree pane).