Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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')),

Copy link
Copy Markdown
Member

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).

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,
),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider defining a widget (e.g. _AccessibilityPanelLabel) for easier reuse:

_AccessibilityPanelLabel(
    label: 'Brightness',
     description: 'Override the color scheme mode of the app.',
)

const SizedBox(height: denseSpacing),
ValueListenableBuilder<String>(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The 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 controller.brightness to be an enum instead of a string to allow for exhaustive checks and prevent any typo bugs.

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;
},
);
},
),
],
);
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,112 @@ void main() {
// Semantics Tree pane should be visible
expect(find.byType(AccessibilitySemanticsTreePane), findsOneWidget);
});

testWidgetsWithWindowSize(
'renders all override controls in AccessibilityOverridesPane',
windowSize,
(WidgetTester tester) async {
await pumpAccessibilityScreen(tester);
await tester.pumpAndSettle();

expect(find.text('Accessibility Overrides'), findsOneWidget);
expect(
find.text(
'Simulate and test accessibility settings on the connected device in real-time.',
),
findsOneWidget,
);

// Brightness controls
expect(find.text('Brightness'), findsOneWidget);
expect(
find.text('Override the color scheme mode of the app.'),
findsOneWidget,
);
expect(
find.byType(RoundedDropDownButton<String>),
findsOneWidget,
);

// Text Scale controls
expect(find.text('Text Scale'), findsOneWidget);
expect(find.text('Scale the system font size.'), findsOneWidget);
expect(find.text('1.00x'), findsOneWidget);
expect(find.byType(Slider), findsOneWidget);

// Switches
expect(find.text('Bold Text'), findsOneWidget);
expect(
find.text('Forces all text in the application to be bold.'),
findsOneWidget,
);
expect(find.text('Screen Reader Debugger'), findsOneWidget);
expect(
find.text('Enables interactive screen reader simulation semantics.'),
findsOneWidget,
);
expect(find.text('High Contrast'), findsOneWidget);
expect(
find.text('Increases the contrast of text and icons.'),
findsOneWidget,
);
expect(find.byType(Switch), findsNWidgets(3));
},
);

testWidgetsWithWindowSize(
'interacting with override controls updates controller state',
windowSize,
(WidgetTester tester) async {
await pumpAccessibilityScreen(tester);
await tester.pumpAndSettle();

Finder findSwitchFor(String label) {
return find.descendant(
of: find.ancestor(
of: find.text(label),
matching: find.byType(Row),
),
matching: find.byType(Switch),
);
}

// 1. Test Brightness Dropdown
expect(controller.brightness.value, 'System');
await tester.tap(find.byType(RoundedDropDownButton<String>));
await tester.pumpAndSettle();
await tester.tap(find.text('Light Mode').last);
await tester.pumpAndSettle();
expect(controller.brightness.value, 'Light');

// 2. Test Bold Text Switch
expect(controller.boldText.value, isFalse);
final boldTextSwitch = findSwitchFor('Bold Text');
await tester.ensureVisible(boldTextSwitch);
await tester.pumpAndSettle();
await tester.tap(boldTextSwitch);
await tester.pumpAndSettle();
expect(controller.boldText.value, isTrue);

// 3. Test Screen Reader Debugger Switch
expect(controller.screenReader.value, isFalse);
final screenReaderSwitch = findSwitchFor('Screen Reader Debugger');
await tester.ensureVisible(screenReaderSwitch);
await tester.pumpAndSettle();
await tester.tap(screenReaderSwitch);
await tester.pumpAndSettle();
expect(controller.screenReader.value, isTrue);

// 4. Test High Contrast Switch
expect(controller.highContrast.value, isFalse);
final highContrastSwitch = findSwitchFor('High Contrast');
await tester.ensureVisible(highContrastSwitch);
await tester.pumpAndSettle();
await tester.tap(highContrastSwitch);
await tester.pumpAndSettle();
expect(controller.highContrast.value, isTrue);
},
);
Comment thread
hannah-hyj marked this conversation as resolved.
});
}

Loading