Skip to content

Commit 895cab5

Browse files
authored
[a11y] fixes overlayPortals not showing VoiceControl labels (#164754)
Fixes flutter/flutter#157753 Even when a `uiaccessibilityelement` is marked as `accessibilityRespondsToUserInteraction`, if it's spatially far away enough from its parent's `accessibilityFrame`, a VoiceControl label will not be created for it. In this PR, I set the parent `SemanticObjectContainer`'s `accessibilityFrame` so it is the minimum rect that will cover all it's children. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing.
1 parent b4a04cc commit 895cab5

File tree

2 files changed

+41
-1
lines changed

2 files changed

+41
-1
lines changed

engine/src/flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.mm

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,12 @@ - (BOOL)isAccessibilityElement {
941941
}
942942

943943
- (CGRect)accessibilityFrame {
944-
return self.semanticsObject.accessibilityFrame;
944+
// For OverlayPortals, the child element is sometimes outside the bounds of the parent
945+
// Even if it's marked accessible, VoiceControl labels will not appear if it's too
946+
// spatially distant. Set the frame to be the max screen size so all children are guaraenteed
947+
// to be contained.
948+
949+
return UIScreen.mainScreen.bounds;
945950
}
946951

947952
- (id)accessibilityContainer {

engine/src/flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTest.mm

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,41 @@ - (void)testFlutterSemanticsObjectMergeTooltipToLabel {
614614
XCTAssertTrue([object.accessibilityLabel isEqualToString:@"label\ntooltip"]);
615615
}
616616

617+
- (void)testSemanticsObjectContainerAccessibilityFrameCoversEntireScreen {
618+
flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
619+
mock->isVoiceOverRunningValue = true;
620+
fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
621+
fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
622+
623+
flutter::SemanticsNode parent;
624+
parent.id = 0;
625+
parent.actions = static_cast<int32_t>(flutter::SemanticsAction::kTap);
626+
627+
flutter::SemanticsNode child;
628+
child.id = 1;
629+
child.actions = static_cast<int32_t>(flutter::SemanticsAction::kTap);
630+
child.rect = SkRect::MakeXYWH(0, 0, 100, 100);
631+
parent.childrenInTraversalOrder.push_back(1);
632+
633+
FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
634+
uid:0];
635+
[parentObject setSemanticsNode:&parent];
636+
637+
FlutterSemanticsObject* childObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
638+
uid:1];
639+
[childObject setSemanticsNode:&child];
640+
641+
parentObject.children = @[ childObject ];
642+
[parentObject accessibilityBridgeDidFinishUpdate];
643+
[childObject accessibilityBridgeDidFinishUpdate];
644+
645+
SemanticsObjectContainer* container =
646+
static_cast<SemanticsObjectContainer*>(parentObject.accessibilityContainer);
647+
648+
XCTAssertTrue(childObject.accessibilityRespondsToUserInteraction);
649+
XCTAssertTrue(CGRectEqualToRect(container.accessibilityFrame, UIScreen.mainScreen.bounds));
650+
}
651+
617652
- (void)testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty {
618653
flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
619654
mock->isVoiceOverRunningValue = true;

0 commit comments

Comments
 (0)