Skip to content

Commit f37ac2a

Browse files
Merge branch 'master' into fix/network-hot-restart
2 parents 2149f89 + e41c353 commit f37ac2a

28 files changed

Lines changed: 860 additions & 17 deletions

CONTRIBUTING.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ as the example Flutter app to connect to when working on DevTools.
162162

163163
## Running and debugging DevTools
164164

165-
Please see [DEBUGGING.md]() for guidance on running and debugging DevTools.
165+
Please see [DEBUGGING.md](DEBUGGING.md) for guidance on running and debugging DevTools.
166166

167167
## Testing for DevTools
168168

169-
Please see [TESTING.md]() for guidance on running and writing tests.
169+
Please see [TESTING.md](TESTING.md) for guidance on running and writing tests.
170170

171171
## Appendix
172172

packages/devtools_app/lib/devtools_app.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export 'src/framework/notifications_view.dart';
1010
export 'src/framework/release_notes.dart';
1111
export 'src/framework/scaffold/app_bar.dart';
1212
export 'src/framework/scaffold/status_line.dart';
13+
export 'src/screens/accessibility/accessibility_controller.dart';
14+
export 'src/screens/accessibility/accessibility_screen.dart';
1315
export 'src/screens/app_size/app_size_controller.dart';
1416
export 'src/screens/app_size/app_size_screen.dart';
1517
export 'src/screens/debugger/breakpoint_manager.dart';

packages/devtools_app/lib/src/app.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import 'framework/notifications_view.dart';
2323
import 'framework/observer/disconnect_observer.dart';
2424
import 'framework/release_notes.dart';
2525
import 'framework/scaffold/scaffold.dart';
26+
import 'screens/accessibility/accessibility_controller.dart';
27+
import 'screens/accessibility/accessibility_screen.dart';
2628
import 'screens/app_size/app_size_controller.dart';
2729
import 'screens/app_size/app_size_screen.dart';
2830
import 'screens/debugger/debugger_controller.dart';
@@ -713,6 +715,11 @@ List<DevToolsScreen> defaultScreens({
713715
LoggingScreen(),
714716
createController: (_) => LoggingController(),
715717
),
718+
if (FeatureFlags.accessibility.isEnabled)
719+
DevToolsScreen<AccessibilityController>(
720+
AccessibilityScreen(),
721+
createController: (_) => AccessibilityController(),
722+
),
716723
DevToolsScreen<DevToolsScreenController>(ProviderScreen()),
717724
DevToolsScreen<AppSizeController>(
718725
AppSizeScreen(),
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
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 'dart:async';
6+
7+
import 'package:devtools_app_shared/service.dart';
8+
import 'package:devtools_app_shared/utils.dart';
9+
import 'package:flutter/foundation.dart';
10+
11+
import '../../service/service_extensions.dart' as extensions;
12+
import '../../shared/framework/screen.dart';
13+
import '../../shared/framework/screen_controllers.dart';
14+
import '../../shared/globals.dart';
15+
16+
/// Modes for brightness override in the accessibility controls.
17+
enum BrightnessOverride {
18+
system('System Default', 'system'),
19+
light('Light Mode', 'Brightness.light'),
20+
dark('Dark Mode', 'Brightness.dark');
21+
22+
const BrightnessOverride(this.display, this.value);
23+
24+
/// The user-facing display label for this override option.
25+
final String display;
26+
27+
/// The raw value associated with this override option sent to or received
28+
/// from the VM service extension.
29+
final String value;
30+
}
31+
32+
/// Controller for the Accessibility screen.
33+
class AccessibilityController extends DevToolsScreenController
34+
with AutoDisposeControllerMixin {
35+
AccessibilityController() {
36+
_initListeners();
37+
}
38+
39+
@override
40+
void init() {
41+
super.init();
42+
_initServiceExtensionStates();
43+
}
44+
45+
void _initListeners() {
46+
addAutoDisposeListener(brightness, _onBrightnessChanged);
47+
addAutoDisposeListener(textScale, _onTextScaleChanged);
48+
addAutoDisposeListener(boldText, _onBoldTextChanged);
49+
addAutoDisposeListener(screenReader, _onScreenReaderChanged);
50+
addAutoDisposeListener(highContrast, _onHighContrastChanged);
51+
}
52+
53+
void _initServiceExtensionStates() {
54+
final state = serviceConnection.serviceManager.serviceExtensionManager
55+
.getServiceExtensionState(extensions.brightnessMode.extension);
56+
57+
void updateFromDeviceState(ServiceExtensionState state) {
58+
final newBrightness = !state.enabled || state.value == null
59+
? BrightnessOverride.system
60+
: BrightnessOverride.values.firstWhere(
61+
(b) => b.value == state.value,
62+
orElse: () => BrightnessOverride.system,
63+
);
64+
brightness.value = newBrightness;
65+
}
66+
67+
updateFromDeviceState(state.value);
68+
addAutoDisposeListener(state, () => updateFromDeviceState(state.value));
69+
}
70+
71+
void _onBrightnessChanged() {
72+
final value = brightness.value;
73+
unawaited(
74+
serviceConnection.serviceManager.serviceExtensionManager
75+
.setServiceExtensionState(
76+
extensions.brightnessMode.extension,
77+
enabled: value != BrightnessOverride.system,
78+
value: value.value,
79+
),
80+
);
81+
}
82+
83+
void _onTextScaleChanged() {
84+
// TODO(hannah-hyj): Implement VM service extension call for text scale override.
85+
}
86+
87+
void _onBoldTextChanged() {
88+
// TODO(hannah-hyj): Implement VM service extension call for bold text override.
89+
}
90+
91+
void _onScreenReaderChanged() {
92+
// TODO(hannah-hyj): Implement VM service extension call for screen reader / semantics debugger.
93+
// e.g. using 'ext.flutter.showSemanticsDebugger'.
94+
}
95+
96+
void _onHighContrastChanged() {
97+
// TODO(hannah-hyj): Implement VM service extension call for high contrast override.
98+
}
99+
100+
@override
101+
final screenId = ScreenMetaData.accessibility.id;
102+
103+
// --- Accessibility Overrides State ---
104+
final brightness = ValueNotifier<BrightnessOverride>(
105+
BrightnessOverride.system,
106+
);
107+
final textScale = ValueNotifier<double>(1.0);
108+
final boldText = ValueNotifier<bool>(false);
109+
final screenReader = ValueNotifier<bool>(false);
110+
final highContrast = ValueNotifier<bool>(false);
111+
112+
@override
113+
void dispose() {
114+
brightness.dispose();
115+
textScale.dispose();
116+
boldText.dispose();
117+
screenReader.dispose();
118+
highContrast.dispose();
119+
super.dispose();
120+
}
121+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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:devtools_app_shared/utils.dart';
7+
import 'package:flutter/material.dart';
8+
9+
import '../../shared/framework/screen.dart';
10+
import '../../shared/globals.dart';
11+
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';
17+
18+
/// A screen that displays accessibility information.
19+
class AccessibilityScreen extends Screen {
20+
AccessibilityScreen() : super.fromMetaData(ScreenMetaData.accessibility);
21+
22+
static final id = ScreenMetaData.accessibility.id;
23+
24+
@override
25+
Widget buildScreenBody(BuildContext context) =>
26+
const AccessibilityScreenBody();
27+
}
28+
29+
class AccessibilityScreenBody extends StatefulWidget {
30+
const AccessibilityScreenBody({super.key});
31+
32+
@override
33+
State<AccessibilityScreenBody> createState() =>
34+
_AccessibilityScreenBodyState();
35+
}
36+
37+
class _AccessibilityScreenBodyState extends State<AccessibilityScreenBody>
38+
with AutoDisposeMixin {
39+
late AccessibilityController controller;
40+
41+
@override
42+
void initState() {
43+
super.initState();
44+
controller = screenControllers.lookup<AccessibilityController>();
45+
}
46+
47+
@override
48+
Widget build(BuildContext context) {
49+
final splitAxis = _splitAxisFor(context);
50+
return SplitPane(
51+
axis: splitAxis,
52+
initialFractions: const [0.6, 0.4],
53+
children: const [
54+
AccessibilitySemanticsTreePane(),
55+
AccessibilityOverridesPane(),
56+
],
57+
);
58+
}
59+
60+
Axis _splitAxisFor(BuildContext context) {
61+
final screenSize = MediaQuery.of(context).size;
62+
return screenSize.width > 1000 ? Axis.horizontal : Axis.vertical;
63+
}
64+
}

0 commit comments

Comments
 (0)