-
Notifications
You must be signed in to change notification settings - Fork 400
[A11y] Create a11y page with placeholders. #9889
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 all commits
a1a77e4
b3681b2
47565eb
f6895d5
9f84565
c0f2ec4
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 |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| // Copyright 2026 The Flutter Authors | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. | ||
|
|
||
| import 'package:devtools_app_shared/utils.dart'; | ||
| import 'package:flutter/foundation.dart'; | ||
|
|
||
| import '../../shared/framework/screen.dart'; | ||
| import '../../shared/framework/screen_controllers.dart'; | ||
|
|
||
| /// Controller for the Accessibility screen. | ||
| class AccessibilityController extends DevToolsScreenController | ||
| with AutoDisposeControllerMixin { | ||
| AccessibilityController() { | ||
| _initListeners(); | ||
| } | ||
|
|
||
| void _initListeners() { | ||
| addAutoDisposeListener(brightness, _onBrightnessChanged); | ||
| addAutoDisposeListener(textScale, _onTextScaleChanged); | ||
| addAutoDisposeListener(boldText, _onBoldTextChanged); | ||
| addAutoDisposeListener(screenReader, _onScreenReaderChanged); | ||
| addAutoDisposeListener(highContrast, _onHighContrastChanged); | ||
| } | ||
|
|
||
| void _onBrightnessChanged() { | ||
| // TODO(hannah-hyj): Implement VM service extension call for brightness override. | ||
| // e.g. using 'ext.flutter.brightnessOverride'. | ||
| } | ||
|
|
||
| void _onTextScaleChanged() { | ||
| // TODO(hannah-hyj): Implement VM service extension call for text scale override. | ||
| } | ||
|
|
||
| void _onBoldTextChanged() { | ||
| // TODO(hannah-hyj): Implement VM service extension call for bold text override. | ||
| } | ||
|
|
||
| void _onScreenReaderChanged() { | ||
| // TODO(hannah-hyj): Implement VM service extension call for screen reader / semantics debugger. | ||
| // e.g. using 'ext.flutter.showSemanticsDebugger'. | ||
| } | ||
|
|
||
| void _onHighContrastChanged() { | ||
| // TODO(hannah-hyj): Implement VM service extension call for high contrast override. | ||
| } | ||
|
|
||
| @override | ||
| final screenId = ScreenMetaData.accessibility.id; | ||
|
|
||
| // --- Accessibility Overrides State --- | ||
| final brightness = ValueNotifier<String>('System'); | ||
| final textScale = ValueNotifier<double>(1.0); | ||
| final boldText = ValueNotifier<bool>(false); | ||
| final screenReader = ValueNotifier<bool>(false); | ||
| final highContrast = ValueNotifier<bool>(false); | ||
|
|
||
| @override | ||
| void dispose() { | ||
| brightness.dispose(); | ||
| textScale.dispose(); | ||
| boldText.dispose(); | ||
| screenReader.dispose(); | ||
| highContrast.dispose(); | ||
| super.dispose(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| // Copyright 2026 The Flutter Authors | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. | ||
|
|
||
| import 'package:devtools_app_shared/ui.dart'; | ||
| import 'package:devtools_app_shared/utils.dart'; | ||
| import 'package:flutter/material.dart'; | ||
|
|
||
| import '../../shared/framework/screen.dart'; | ||
| import '../../shared/globals.dart'; | ||
| import '../../shared/ui/common_widgets.dart'; | ||
| import 'accessibility_controller.dart'; | ||
|
|
||
| /// A screen that displays accessibility information. | ||
| class AccessibilityScreen extends Screen { | ||
| AccessibilityScreen() : super.fromMetaData(ScreenMetaData.accessibility); | ||
|
|
||
| static final id = ScreenMetaData.accessibility.id; | ||
|
|
||
| @override | ||
| Widget buildScreenBody(BuildContext context) => | ||
| const AccessibilityScreenBody(); | ||
| } | ||
|
|
||
| class AccessibilityScreenBody extends StatefulWidget { | ||
| const AccessibilityScreenBody({super.key}); | ||
|
|
||
| @override | ||
| State<AccessibilityScreenBody> createState() => | ||
| _AccessibilityScreenBodyState(); | ||
| } | ||
|
|
||
| class _AccessibilityScreenBodyState extends State<AccessibilityScreenBody> | ||
| with AutoDisposeMixin { | ||
| late AccessibilityController controller; | ||
|
|
||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| controller = screenControllers.lookup<AccessibilityController>(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| final splitAxis = _splitAxisFor(context); | ||
| return SplitPane( | ||
| axis: splitAxis, | ||
| initialFractions: const [0.6, 0.4], | ||
| children: const [ | ||
| AccessibilitySemanticsTreePane(), | ||
| AccessibilityOverridesPane(), | ||
| ], | ||
| ); | ||
| } | ||
|
|
||
| Axis _splitAxisFor(BuildContext context) { | ||
| final screenSize = MediaQuery.of(context).size; | ||
| return screenSize.width > 1000 ? Axis.horizontal : Axis.vertical; | ||
| } | ||
| } | ||
|
|
||
| /// A pane that displays the semantics tree of the connected app. | ||
| class AccessibilitySemanticsTreePane extends StatelessWidget { | ||
| const AccessibilitySemanticsTreePane({super.key}); | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return const DevToolsAreaPane( | ||
| header: AreaPaneHeader(title: Text('Semantics Tree')), | ||
| child: CenteredMessage( | ||
| message: | ||
| 'Accessibility semantics tree placeholder.\n' | ||
| '// TODO(hannah-hyj): Implement semantics tree view and details explorer.', | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| /// A pane that displays the accessibility overrides controls. | ||
| class AccessibilityOverridesPane extends StatelessWidget { | ||
| const AccessibilityOverridesPane({super.key}); | ||
|
|
||
| @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).', | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -94,6 +94,12 @@ enum ScreenMetaData { | |
| supportsWebServerDevice: true, | ||
| tutorialVideoTimestamp: '?t=558', | ||
| ), | ||
| accessibility( | ||
| 'accessibility', | ||
| title: 'Accessibility', | ||
| icon: Icons.accessibility, | ||
| requiresFlutter: true, | ||
|
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. does this page work for flutter web apps or should it be disabled for web?
Member
Author
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. web apps also have semantics tree and a11y settings. This page should work. |
||
| ), | ||
|
hannah-hyj marked this conversation as resolved.
|
||
| provider( | ||
| 'provider', | ||
| title: 'Provider', | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // Copyright 2026 The Flutter Authors | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd. | ||
|
|
||
| @TestOn('vm') | ||
| library; | ||
|
|
||
| import 'package:devtools_app/devtools_app.dart'; | ||
| import 'package:devtools_app_shared/ui.dart'; | ||
| import 'package:devtools_app_shared/utils.dart'; | ||
| import 'package:devtools_test/devtools_test.dart'; | ||
| import 'package:devtools_test/helpers.dart'; | ||
| import 'package:flutter/material.dart'; | ||
| import 'package:flutter_test/flutter_test.dart'; | ||
| import 'package:mockito/mockito.dart'; | ||
|
|
||
| void main() { | ||
| late AccessibilityScreen screen; | ||
| late AccessibilityController controller; | ||
| const windowSize = Size(1000.0, 1000.0); | ||
|
|
||
| group('Accessibility Screen', () { | ||
| Future<void> pumpAccessibilityScreen(WidgetTester tester) async { | ||
| await tester.pumpWidget( | ||
| wrapWithControllers( | ||
| const AccessibilityScreenBody(), | ||
| accessibility: controller, | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| setUp(() { | ||
| final fakeServiceConnection = FakeServiceConnectionManager(); | ||
| when( | ||
| fakeServiceConnection.serviceManager.connectedApp!.isFlutterWebAppNow, | ||
| ).thenReturn(false); | ||
| when( | ||
| fakeServiceConnection.serviceManager.connectedApp!.isProfileBuildNow, | ||
| ).thenReturn(false); | ||
| when( | ||
| fakeServiceConnection.errorBadgeManager.errorCountNotifier( | ||
| 'accessibility', | ||
| ), | ||
| ).thenReturn(ValueNotifier<int>(0)); | ||
|
|
||
| setGlobal(NotificationService, NotificationService()); | ||
| setGlobal( | ||
| DevToolsEnvironmentParameters, | ||
| ExternalDevToolsEnvironmentParameters(), | ||
| ); | ||
| setGlobal(PreferencesController, PreferencesController()); | ||
| setGlobal(ServiceConnectionManager, fakeServiceConnection); | ||
| setGlobal(IdeTheme, IdeTheme()); | ||
|
|
||
| controller = AccessibilityController(); | ||
| screen = AccessibilityScreen(); | ||
| }); | ||
|
|
||
| testWidgets('builds its tab', (WidgetTester tester) async { | ||
| await tester.pumpWidget(wrap(Builder(builder: screen.buildTab))); | ||
| expect(find.text('Accessibility'), findsOneWidget); | ||
| }); | ||
|
|
||
| testWidgetsWithWindowSize('builds split view with panes', windowSize, ( | ||
| WidgetTester tester, | ||
| ) async { | ||
| await pumpAccessibilityScreen(tester); | ||
| await tester.pumpAndSettle(); | ||
|
|
||
| expect(find.byType(AccessibilityScreenBody), findsOneWidget); | ||
| expect(find.byType(SplitPane), findsAtLeastNWidgets(1)); | ||
|
|
||
| // Overrides pane should be visible | ||
| expect(find.byType(AccessibilityOverridesPane), findsOneWidget); | ||
|
|
||
| // Semantics Tree pane should be visible | ||
| expect(find.byType(AccessibilitySemanticsTreePane), findsOneWidget); | ||
| }); | ||
| }); | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.