mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web] move AccessibilityAnnouncements into SemanticsOwner (flutter/engine#52138)
Move `AccessibilityAnnouncements` into `SemanticsOwner`, effectively making it a singleton (because `SemanticsOwner` is). Fixes https://github.com/flutter/flutter/issues/139272
This commit is contained in:
parent
e1b38345d3
commit
2affc5230f
@ -688,9 +688,10 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
|
||||
case 'flutter/accessibility':
|
||||
// In widget tests we want to bypass processing of platform messages.
|
||||
const StandardMessageCodec codec = StandardMessageCodec();
|
||||
// TODO(yjbanov): Dispatch the announcement to the correct view?
|
||||
// https://github.com/flutter/flutter/issues/137445
|
||||
implicitView?.accessibilityAnnouncements.handleMessage(codec, data);
|
||||
final EngineSemantics semantics = EngineSemantics.instance;
|
||||
if (semantics.semanticsEnabled) {
|
||||
semantics.accessibilityAnnouncements.handleMessage(codec, data);
|
||||
}
|
||||
replyToPlatformMessage(callback, codec.encodeMessage(true));
|
||||
return;
|
||||
|
||||
|
||||
@ -4,7 +4,6 @@
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../platform_dispatcher.dart';
|
||||
import 'accessibility.dart';
|
||||
import 'label_and_value.dart';
|
||||
import 'semantics.dart';
|
||||
@ -32,7 +31,7 @@ class LiveRegion extends RoleManager {
|
||||
|
||||
AccessibilityAnnouncements get _accessibilityAnnouncements =>
|
||||
_accessibilityAnnouncementsOverride ??
|
||||
EnginePlatformDispatcher.instance.implicitView!.accessibilityAnnouncements;
|
||||
EngineSemantics.instance.accessibilityAnnouncements;
|
||||
|
||||
@override
|
||||
void update() {
|
||||
|
||||
@ -18,6 +18,7 @@ import '../platform_dispatcher.dart';
|
||||
import '../util.dart';
|
||||
import '../vector_math.dart';
|
||||
import '../window.dart';
|
||||
import 'accessibility.dart';
|
||||
import 'checkable.dart';
|
||||
import 'dialog.dart';
|
||||
import 'focusable.dart';
|
||||
@ -1938,6 +1939,19 @@ class EngineSemantics {
|
||||
|
||||
static EngineSemantics? _instance;
|
||||
|
||||
/// The tag name for the accessibility announcements host.
|
||||
static const String announcementsHostTagName = 'flt-announcement-host';
|
||||
|
||||
/// Implements verbal accessibility announcements.
|
||||
final AccessibilityAnnouncements accessibilityAnnouncements =
|
||||
AccessibilityAnnouncements(hostElement: _initializeAccessibilityAnnouncementHost());
|
||||
|
||||
static DomElement _initializeAccessibilityAnnouncementHost() {
|
||||
final DomElement host = createDomElement(announcementsHostTagName);
|
||||
domDocument.body!.append(host);
|
||||
return host;
|
||||
}
|
||||
|
||||
/// Disables semantics and uninitializes the singleton [instance].
|
||||
///
|
||||
/// Instances of [EngineSemanticsOwner] are no longer valid after calling this
|
||||
|
||||
@ -26,8 +26,6 @@ import 'style_manager.dart';
|
||||
/// | | | |
|
||||
/// | | | +- <flt-scene>
|
||||
/// | | |
|
||||
/// | | +- [announcementsHost] <flt-announcement-host>
|
||||
/// | | |
|
||||
/// | | +- <style>
|
||||
/// | |
|
||||
/// | +- ...platform views
|
||||
@ -50,7 +48,6 @@ class DomManager {
|
||||
final DomElement sceneHost = domDocument.createElement(DomManager.sceneHostTagName);
|
||||
final DomElement textEditingHost = domDocument.createElement(DomManager.textEditingHostTagName);
|
||||
final DomElement semanticsHost = domDocument.createElement(DomManager.semanticsHostTagName);
|
||||
final DomElement announcementsHost = createDomElement(DomManager.announcementsHostTagName);
|
||||
|
||||
// Root element children.
|
||||
rootElement.appendChild(platformViewsHost);
|
||||
@ -71,7 +68,6 @@ class DomManager {
|
||||
// Rendering host (shadow root) children.
|
||||
|
||||
renderingHost.append(sceneHost);
|
||||
renderingHost.append(announcementsHost);
|
||||
|
||||
// Styling.
|
||||
|
||||
@ -106,7 +102,6 @@ class DomManager {
|
||||
sceneHost: sceneHost,
|
||||
textEditingHost: textEditingHost,
|
||||
semanticsHost: semanticsHost,
|
||||
announcementsHost: announcementsHost,
|
||||
);
|
||||
}
|
||||
|
||||
@ -117,7 +112,6 @@ class DomManager {
|
||||
required this.sceneHost,
|
||||
required this.textEditingHost,
|
||||
required this.semanticsHost,
|
||||
required this.announcementsHost,
|
||||
});
|
||||
|
||||
/// The tag name for the Flutter View root element.
|
||||
@ -135,9 +129,6 @@ class DomManager {
|
||||
/// The tag name for the semantics host.
|
||||
static const String semanticsHostTagName = 'flt-semantics-host';
|
||||
|
||||
/// The tag name for the accessibility announcements host.
|
||||
static const String announcementsHostTagName = 'flt-announcement-host';
|
||||
|
||||
/// The root DOM element for the entire Flutter View.
|
||||
///
|
||||
/// This is where input events are captured, such as pointer events.
|
||||
@ -168,9 +159,6 @@ class DomManager {
|
||||
/// around the UI.
|
||||
final DomElement semanticsHost;
|
||||
|
||||
/// This is where accessibility announcements are inserted.
|
||||
final DomElement announcementsHost;
|
||||
|
||||
DomElement? _lastSceneElement;
|
||||
|
||||
/// Inserts the [sceneElement] into the DOM and removes the existing scene (if
|
||||
|
||||
@ -135,11 +135,6 @@ base class EngineFlutterView implements ui.FlutterView {
|
||||
semantics.updateSemantics(update);
|
||||
}
|
||||
|
||||
// TODO(yjbanov): How should this look like for multi-view?
|
||||
// https://github.com/flutter/flutter/issues/137445
|
||||
late final AccessibilityAnnouncements accessibilityAnnouncements =
|
||||
AccessibilityAnnouncements(hostElement: dom.announcementsHost);
|
||||
|
||||
late final GlobalHtmlAttributes _globalHtmlAttributes = GlobalHtmlAttributes(
|
||||
rootElement: dom.rootElement,
|
||||
hostElement: embeddingStrategy.hostElement,
|
||||
|
||||
@ -229,6 +229,30 @@ void _testEngineSemanticsOwner() {
|
||||
expect(semantics().mode, AccessibilityMode.unknown);
|
||||
});
|
||||
|
||||
// Expecting the following DOM structure by default:
|
||||
//
|
||||
// <body>
|
||||
// <flt-announcement-host>
|
||||
// <flt-announcement-polite></flt-announcement-polite>
|
||||
// <flt-announcement-assertive></flt-announcement-assertive>
|
||||
// </flt-announcement-host>
|
||||
// </body>
|
||||
test('places accessibility announcements in the <body> tag', () {
|
||||
final AccessibilityAnnouncements accessibilityAnnouncements = semantics().accessibilityAnnouncements;
|
||||
final DomElement politeElement = accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.polite);
|
||||
final DomElement assertiveElement = accessibilityAnnouncements.ariaLiveElementFor(Assertiveness.assertive);
|
||||
final DomElement announcementHost = politeElement.parent!;
|
||||
|
||||
// Polite and assertive elements share the same host.
|
||||
expect(
|
||||
assertiveElement.parent,
|
||||
announcementHost,
|
||||
);
|
||||
|
||||
// The host is a direct child of <body>
|
||||
expect(announcementHost.parent, domDocument.body);
|
||||
});
|
||||
|
||||
test('accessibilityFeatures copyWith function works', () {
|
||||
const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0);
|
||||
EngineAccessibilityFeatures copy =
|
||||
|
||||
@ -27,7 +27,6 @@ void doTests() {
|
||||
expect(domManager.platformViewsHost.tagName, equalsIgnoringCase(DomManager.glassPaneTagName));
|
||||
expect(domManager.textEditingHost.tagName, equalsIgnoringCase(DomManager.textEditingHostTagName));
|
||||
expect(domManager.semanticsHost.tagName, equalsIgnoringCase(DomManager.semanticsHostTagName));
|
||||
expect(domManager.announcementsHost.tagName, equalsIgnoringCase(DomManager.announcementsHostTagName));
|
||||
|
||||
// Check parent-child relationships.
|
||||
|
||||
@ -39,10 +38,9 @@ void doTests() {
|
||||
expect(rootChildren[3].tagName, equalsIgnoringCase('style'));
|
||||
|
||||
final List<DomElement> shadowChildren = domManager.renderingHost.childNodes.cast<DomElement>().toList();
|
||||
expect(shadowChildren.length, 3);
|
||||
expect(shadowChildren.length, 2);
|
||||
expect(shadowChildren[0], domManager.sceneHost);
|
||||
expect(shadowChildren[1], domManager.announcementsHost);
|
||||
expect(shadowChildren[2].tagName, equalsIgnoringCase('style'));
|
||||
expect(shadowChildren[1].tagName, equalsIgnoringCase('style'));
|
||||
});
|
||||
|
||||
test('hide placeholder text for textfield', () {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user