Skip to content

[iOS] Rebind accessibility bridge after view controller changes - #187167

Merged
auto-submit[bot] merged 28 commits into
flutter:masterfrom
smocer:ios-accessibility-bridge-reattach
Sep 21, 2026
Merged

auto-submit[bot] merged 28 commits into
flutter:masterfrom
smocer:ios-accessibility-bridge-reattach

Conversation

@smocer

@smocer smocer commented May 27, 2026 •

Copy link
Copy Markdown
Contributor

Keep the existing AccessibilityBridge when PlatformViewIOS detaches from an owner FlutterViewController, and rebind it when a controller/view is attached again.

In add-to-app integrations that reuse a FlutterEngine, the engine can detach from one FlutterViewController and later attach to another one. When semantics are already enabled, clearing the bridge during detach drops the UIKit accessibility representation of the current semantics tree. The Flutter UI continues to render, but VoiceOver / Accessibility Inspector can no longer see Flutter fields and buttons after reattachment.

This change preserves the bridge across owner-controller detach/reattach, skips semantics updates while no owner controller is attached, and rebinds the bridge to the new owner controller/view before semantics updates resume.

Fixes #186582

Pre-launch Checklist

@smocer
smocer requested a review from a team as a code owner May 27, 2026 12:16
@google-cla

google-cla Bot commented May 27, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

Gemini encountered an error creating the review. You can try again by commenting /gemini review.

@github-actions github-actions Bot added platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) team-ios Owned by iOS platform team labels May 27, 2026
@smocer
smocer marked this pull request as draft May 27, 2026 12:17
@smocer
smocer force-pushed the ios-accessibility-bridge-reattach branch from 551517b to 59d8bdb Compare May 27, 2026 18:04
@smocer
smocer marked this pull request as ready for review May 27, 2026 18:07
@smocer

smocer commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request enables the iOS accessibility bridge to be rebound to sequential view controllers and views, ensuring that native accessibility elements like scroll views are correctly reattached. The review feedback highlights a critical issue where semantics updates could be discarded while the view controller is detached, leading to state desynchronization, and suggests an optimization for weak pointer copies and direct instance variable access in SemanticsObject.mm.

Comment on lines +158 to +163
if (!accessibility_bridge_ || !owner_controller_ || !owner_controller_.isViewLoaded) {
return;
}
accessibility_bridge_.get()->UpdateSemantics(std::move(update), actions);
[[NSNotificationCenter defaultCenter] postNotificationName:FlutterSemanticsUpdateNotification
object:owner_controller_];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Returning early when owner_controller_ is nil or !owner_controller_.isViewLoaded will completely discard any semantics updates sent by the Flutter framework while the view controller is detached or before its view is loaded. This causes the AccessibilityBridge's internal objects_ cache to become out of sync with the Flutter framework's actual semantics tree. When the view controller is later reattached or its view is loaded, the bridge will populate the view with stale semantics data.

Since AccessibilityBridge::UpdateSemantics is already safe to call when view_controller_ or view_ is nil (it will update the internal objects_ cache but skip updating the non-existent view or posting notifications), we should always forward the updates to the bridge if it exists, and only guard the NSNotificationCenter post.

  if (!accessibility_bridge_) {
    return;
  }
  accessibility_bridge_->UpdateSemantics(std::move(update), actions);
  if (owner_controller_ && owner_controller_.isViewLoaded) {
    [[NSNotificationCenter defaultCenter] postNotificationName:FlutterSemanticsUpdateNotification
                                                        object:owner_controller_];
  }
References
  1. Avoid duplicating state and keep only one source of truth by ensuring the accessibility bridge's internal cache is always kept in sync with the Flutter framework's semantics tree. (link)

Comment on lines +186 to +192
UIView* view = [self isAccessibilityBridgeAlive] ? self.bridge->viewIfLoaded() : nil;
if (self.scrollView.superview != view) {
[self.scrollView removeFromSuperview];
if (view) {
[view addSubview:self.scrollView];
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

We can optimize this method by avoiding redundant copies of the weak pointer self.bridge and bypassing the isAccessibilityBridgeAlive helper (which also copies the weak pointer). Additionally, accessing the backing instance variable _scrollView directly is more efficient and idiomatic in Objective-C than using the property getter self.scrollView within the class implementation.

  auto bridge = self.bridge;
  UIView* view = bridge ? bridge->viewIfLoaded() : nil;
  if (_scrollView.superview != view) {
    [_scrollView removeFromSuperview];
    if (view) {
      [view addSubview:_scrollView];
    }
  }
References
  1. Optimize for readability and performance by avoiding redundant weak pointer copies and using direct instance variable access. (link)

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the iOS platform view and accessibility bridge to support rebinding the accessibility bridge to sequential FlutterViewController and FlutterView instances when the owner controller is reattached. The reviewer identified an issue where skipping semantics updates while the view controller is detached or not loaded can cause the internal semantics tree to become out of sync. They recommend always forwarding semantics updates to the AccessibilityBridge and only guarding the notification post, as well as updating the corresponding unit tests to assert that updates are preserved while detached.

Comment thread engine/src/flutter/shell/platform/darwin/ios/platform_view_ios.mm Outdated
Comment thread engine/src/flutter/shell/platform/darwin/ios/platform_view_ios_test.mm Outdated
Keep the existing AccessibilityBridge when PlatformViewIOS detaches from an owner FlutterViewController, and rebind it when a controller/view is attached again. This preserves the semantics tree after engines are reused across FlutterViewController instances.

Skip semantics updates while no owner controller is attached because UIKit accessibility elements need a valid container.

Add iOS unit coverage for detached semantics updates and reattaching a new owner controller.
@smocer
smocer force-pushed the ios-accessibility-bridge-reattach branch from 59d8bdb to 43441f8 Compare May 27, 2026 18:47
@smocer

smocer commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the iOS accessibility bridge lifecycle to support rebinding to sequential FlutterViewController and FlutterView instances. Instead of resetting the accessibility bridge when a view controller is detached, the bridge is preserved and updated with the new view controller via the newly introduced SetViewController method. This change ensures that semantics updates are preserved while detached and that native UIKit views (like scroll views) are correctly reattached to the active view. Comprehensive unit tests have been added to verify these rebinding behaviors and prevent regressions. I have no additional feedback to provide as there are no review comments.

@LouiseHsu LouiseHsu added the CICD Run CI/CD label May 27, 2026
LouiseHsu
LouiseHsu previously approved these changes Jun 11, 2026

@LouiseHsu LouiseHsu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

lgtm!

if (!semantics_tree_enabled_) {
return;
}
FlutterPlatformViewsController* platform_views_controller =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

tiny optional nit:

FlutterPlatformViewsController* platform_views_controller =
      owner_controller_.platformViewsController ?: platform_views_controller_;

is slightly cleaner

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@LouiseHsu Addressed, please reapprove

@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 11, 2026
@smocer
smocer requested a review from LouiseHsu June 11, 2026 22:20
@Hari-07 Hari-07 added the CICD Run CI/CD label Jun 23, 2026
@cbracken cbracken added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 18, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 18, 2026
@auto-submit

auto-submit Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/187167, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@auto-submit

auto-submit Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/187167, because This PR has not met approval requirements for merging. The PR author is not a member of flutter-hackers and needs 1 more review(s) in order to merge this PR.

  • Merge guidelines: A PR needs at least one approved review if the author is already part of flutter-hackers or two member reviews if the author is not a member of flutter-hackers before re-applying the autosubmit label. Reviewers: If you left a comment approving, please use the "approve" review action instead.

@cbracken cbracken left a comment

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.

LGTM stamp from a Japanese personal seal

@cbracken cbracken added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 18, 2026
@auto-submit

auto-submit Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/187167, because The base commit of the PR is older than 7 days and can not be merged. Please merge the latest changes from the main into this branch and resubmit the PR.

@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 18, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 18, 2026
@cbracken cbracken added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 18, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Sep 19, 2026
@auto-submit

auto-submit Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/187167, because - The status or check suite Tree_analyze has failed. Please fix the issues identified (or deflake) before re-applying this label.

@LongCatIsLooong LongCatIsLooong added the CICD Run CI/CD label Sep 19, 2026
@flutter-dashboard

Copy link
Copy Markdown

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, commenting will be silenced, and the check will not try to resolve itself until marked ready for review.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

Changes reported for pull request #187167 at sha f88712e

@flutter-dashboard flutter-dashboard Bot added the will affect goldens Changes to golden files label Sep 19, 2026
@smocer

smocer commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

@cbracken @LongCatIsLooong It looks like cupertino.cupertinoActionSheet.press-drag is an unrelated flaky golden, not our regression. And the first job just timed out. Need to rerun those

@LongCatIsLooong LongCatIsLooong added the autosubmit Merge PR when tree becomes green via auto submit App label Sep 19, 2026
@auto-submit

auto-submit Bot commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/187167, because - The status or check suite Tree_analyze has failed. Please fix the issues identified (or deflake) before re-applying this label.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) CICD Run CI/CD engine flutter/engine related. See also e: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team triaged-accessibility Triaged by Framework Accessibility team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[iOS] Accessibility tree is lost after reusing a FlutterEngine with a new FlutterViewController

8 participants