Skip to content

[ios] Use DFS traversal to find engines of a scene automatically (rather than manually manage the engines list) - #192457

Merged
auto-submit[bot] merged 7 commits into
flutter:masterfrom
hellohuanlin:simplify_uiscene
Oct 16, 2026
Merged

auto-submit[bot] merged 7 commits into
flutter:masterfrom
hellohuanlin:simplify_uiscene

Conversation

@hellohuanlin

@hellohuanlin hellohuanlin commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

This PR simplifies iOS embedder's UIScene lifecycle logic. The key idea is to perform DFS traversal to find engines to send scene events to (rather than manually manage the flutterManagedEngines list).

By doing so, we are able to remove the need to manual registering engines (mostly, see below) and other logics too.

This PR also fixes a few bugs along the way for both single and multi-scenes.

Simplifications:

[multi-scene] Use DFS to find engines of a scene automatically (rather than manually manage flutterManagedEngines list)
[multi-scene] Manual register/unregister is only required for cases where FlutterVC is not added to the scene but still wanna receive events (e.g. headless/add-to-app) (Fixes #185689)
[single-scene] Remove NSNotification code path for connection events

Bug fixes

[single-scene] Fixes missing connection event for an edge case of implicit engine usage (Fixes #186547)
[single-scene] Fix missing lifecycle events if the engine is not attached to any scenes (e.g. headless engines/add-to-app) (Fixes #191907)
[multi-scene] connectionOptions should be per scene (Fixes #191039)
[multi-scene] Manual register/unregister API is now registered per scene rather than per scene delegate (added scene parameter)
[multi-scene] Scene lifecycle listeners are not per-scene rather than per scene delegate (Fixes #185687)

Also fixes #185688
Also fixes #185702
Also fixes #192344

If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

@github-actions github-actions Bot added platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. team-ios Owned by iOS platform team labels Sep 9, 2026
@hellohuanlin hellohuanlin added the CICD Run CI/CD label Sep 9, 2026
'-destination',
'id=$deviceId',
'-derivedDataPath',
derivedDataPath,

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.

this fixes #192344

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.

nit: Doesn't matter that much, but probably better location would be in the app's build directory

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.

updated to derivedDataPath = path.join(appDir.path, 'build', 'DerivedData');

button.tap()

let expectedEventsAfterBackgroundAndReactivate = [
"sceneWillConnect",

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.

this fixes #186547

super.viewDidLoad()
view.backgroundColor = .white

// Create FlutterViewController with engine, but NEVER add it to the view hierarchy or as a child VC.

@hellohuanlin hellohuanlin Sep 9, 2026 •

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.

It turns out that we still have to have register/unregister APIs, but we only need it for edge cases when Flutter VC is not added on the screen. This is the test for it.

See #185689

FLUTTER_DARWIN_EXPORT
@interface FlutterSceneDelegate
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistration>
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistry>

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.

Since no one is using old API, I will just replace it (rather than going the proper deprecate-then-delete route). Renaming the protocol since we added a new scene parameter.

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.

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.

Since we need to keep the old API, I wonder if it can maintain its current behavior, which is to receive and forwards all events received by the SceneDelegate even though it may be sent to multiple scenes (if the SceneDelegate has multiple scenes).

We can still deprecate it and encourage the new API but I think making it just a no-op is a breaking change.

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.

I've also been doing some research and I haven't seen any evidence that a SceneDelegate can support multiple scenes. It seems like a 1-to-1 relationship. I tried to create a native app with multiple scenes per delegate and could not figure out a way to do it

@hellohuanlin hellohuanlin Sep 29, 2026 •

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.

I've also been doing some research and I haven't seen any evidence that a SceneDelegate can support multiple scenes. It seems like a 1-to-1 relationship. I tried to create a native app with multiple scenes per delegate and could not figure out a way to do it

You can create multiple scenes powered by the same scene delegate by calling activateSceneSession multiple times.

Actually the easiest way is just to set the UIScene.delegate property to a shared delegate object: https://developer.apple.com/documentation/uikit/uiscene/delegate

You can change this default delegate object later, as needed.

@hellohuanlin hellohuanlin Oct 10, 2026 •

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.

Added back the old API and preserved old behavior (also added back the integration test)

self.sceneLifeCycleDelegate = [[FlutterEnginePluginSceneLifeCycleDelegate alloc] init];

if (!FlutterSharedApplication.application.supportsMultipleScenes) {
[FlutterPluginSceneLifeCycleDelegate registerEngineForSingleScene:self];

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.

This also fixes #191907 (issue with using NSNotification)

// Tracks whether the application-level sceneWillConnect fallback (which forwards to
// application:didFinishLaunchingWithOptions:) has already been invoked. This must only be
// called once per application lifetime.
static BOOL gSceneWillConnectFallbackCalled = NO;

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.

Used to be scene-delegate-scoped, now it's app-scoped

* This array is lazily cleaned up. `updateFlutterManagedEnginesInScene:` should be called before
* use to ensure it is up-to-date.
*/
@property(nonatomic, strong) NSPointerArray* flutterManagedEngines;

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.

We now use DFS rather than manually managing this list

// Once the connection window ends on the next runloop, entries are removed.
// This is to ensure engines attached after sceneWillConnect (but still in the same runloop)
// still receive their sceneWillConnect event (e.g. viewDidLoad of the root view controller).
@property(nonatomic, strong) NSMapTable<UIScene*, UISceneConnectionOptions*>* connectingScenes;

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.

used to be scene-delegate scoped, now it's scene-scoped

* available. It may not be available if embedded in an iOS app extension or the AppDelegate doesn't
* subclass FlutterAppDelegate.
*/
- (FlutterPluginAppLifeCycleDelegate*)applicationLifeCycleDelegate {

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.

this is moved code

@hellohuanlin
hellohuanlin marked this pull request as ready for review September 9, 2026 18:41
@hellohuanlin
hellohuanlin requested a review from a team as a code owner September 9, 2026 18:41

@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 iOS scene lifecycle tracking in Flutter, introducing scene-specific manual engine registration and improving automatic discovery of view controllers in multi-scene applications. The review feedback identifies thread-safety concerns with concurrent access to the global gEnginesForSingleScene array, a potential unintended engine registration when running in environments where UIApplication is unavailable, and potential memory leaks in Swift integration test plugins due to strong static references to plugin instances.

Comment on lines +288 to +290
if (!FlutterSharedApplication.application.supportsMultipleScenes) {
[FlutterPluginSceneLifeCycleDelegate registerEngineForSingleScene:self];
}

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

If FlutterSharedApplication.isAvailable is false (e.g., when running in an app extension), FlutterSharedApplication.application will be nil. In Objective-C, messaging nil returns NO, which means !supportsMultipleScenes will evaluate to YES, and the engine will be unnecessarily registered for the single scene. We should guard this registration with FlutterSharedApplication.isAvailable.

  if (FlutterSharedApplication.isAvailable && !FlutterSharedApplication.application.supportsMultipleScenes) {
    [FlutterPluginSceneLifeCycleDelegate registerEngineForSingleScene:self];
  }
References
  1. Avoid unnecessary operations and potential nil-messaging side effects by guarding application-specific logic with availability checks.

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.

Hmmmm, I borrowed this check from the NSNotification call path. Worth thinking about it

@hellohuanlin hellohuanlin changed the title [DRAFT] Simplify uiscene [ios] Use DFS traversal to find engines of a scene automatically (rather than manually manage the engines list) Sep 9, 2026
FLUTTER_DARWIN_EXPORT
@interface FlutterSceneDelegate
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistration>
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistry>

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.

[self searchFlutterViewControllersWithResult:result
visited:visited
viewController:viewController.presentedViewController];
}

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.

Do you think there's any concern in Apple introducing new APIs that this doesn't account for?

For example, UIArrangementViewController was added in 27.1 beta. One would guess that the "container view controllers" will be found via childViewControllers, but what if they're not and need to accessed differently such as with the presentedViewController?

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.

If apple introduces a new relationship between VCs, then yes, we have to update this code. However, this hasn't changed for like 15+ years.

The UIArrangementViewController should still use child/parent relationship since it's a container (just like a UINavigationController).

Comment on lines +284 to +293
// Close the connection window on the next run loop turn.
__weak __typeof(self) weakSelf = self;
__weak __typeof(scene) weakScene = scene;
dispatch_async(dispatch_get_main_queue(), ^{
__typeof(self) strongSelf = weakSelf;
__typeof(scene) strongScene = weakScene;
if (strongSelf && strongScene) {
[strongSelf.connectingScenes removeObjectForKey:strongScene];
}
});

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.

Can you explain why this is needed?

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.

Updated with more comments.

FLUTTER_DARWIN_EXPORT
@interface FlutterSceneDelegate
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistration>
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistry>

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.

Since we need to keep the old API, I wonder if it can maintain its current behavior, which is to receive and forwards all events received by the SceneDelegate even though it may be sent to multiple scenes (if the SceneDelegate has multiple scenes).

We can still deprecate it and encourage the new API but I think making it just a no-op is a breaking change.

FLUTTER_DARWIN_EXPORT
@interface FlutterSceneDelegate
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistration>
: NSObject <UIWindowSceneDelegate, FlutterSceneLifeCycleEngineRegistry>

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.

I've also been doing some research and I haven't seen any evidence that a SceneDelegate can support multiple scenes. It seems like a 1-to-1 relationship. I tried to create a native app with multiple scenes per delegate and could not figure out a way to do it

// Tracks all engines for single-scene applications.
// We have to use a global variable because the FlutterEngine could be created before
// UISceneDelegate object is created.
static NSPointerArray* gEnginesForSingleScene;

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.

Perhaps consider using a singleton?

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.

Technically speaking this is a singleton.

However, I do notice that in other files we put these values scoped under a class, so I updated to follow existing convention.

}

[self compactNSPointerArray:self.developerManagedEngines];
- (NSArray<FlutterViewController*>*)searchFlutterViewControllersWithScene:(UIScene*)scene {

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.

Nit:

Suggested change
- (NSArray<FlutterViewController*>*)searchFlutterViewControllersWithScene:(UIScene*)scene {
- (NSArray<FlutterViewController*>*)searchFlutterViewControllersWithinScene:(UIScene*)scene {

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.

done

}

engine.manuallyRegisteredToScene = YES;
- (NSArray<FlutterEngine*>*)searchFlutterEnginesWithScene:(UIScene*)scene {

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.

Nit:

Suggested change
- (NSArray<FlutterEngine*>*)searchFlutterEnginesWithScene:(UIScene*)scene {
- (NSArray<FlutterEngine*>*)searchFlutterEnginesWithinScene:(UIScene*)scene {

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.

done

// called once per application lifetime.
static BOOL gSceneWillConnectFallbackCalled = NO;

static BOOL IsPowerOfTwo(NSUInteger x) {

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.

Can you add a comment on what this does and why it's required?

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.

done

* This method is **only** necessary in multi-scene applications when an engine needs to receive
* a scene's life-cycle events before its `FlutterViewController` is added to that scene (for
* example, pre-warmed engines in add-to-app workflows), or when the engine has no view controller
* at all (headless engines).

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.

Nit: I personally like the format where it's clearly outlined when this should be used

This method is **only** necessary when the following conditions are true:
 * 1. x
 * 2. y
 * 3. z

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.

done

@hellohuanlin
hellohuanlin force-pushed the simplify_uiscene branch 2 times, most recently from f2f2425 to fb339dd Compare October 12, 2026 00:25
* created, so manual registration is never required.
*/
@protocol FlutterSceneLifeCycleEngineRegistration
@protocol FlutterSceneLifeCycleEngineRegistry

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.

Does this need to be a new protocol? Can we just keep the old protocol and make the methods optional? What's the pros and cons?

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.

Making these methods optional would be wrong semantics - they are both mandatory for a functional registry. Leaving them optional also introduces confusing edge cases, such as an implementation providing register while omitting unregister.

@hellohuanlin hellohuanlin added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 16, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 16, 2026
Merged via the queue into flutter:master with commit 4ba130c Oct 16, 2026
23 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 16, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Oct 16, 2026
flutter/flutter@5c7e45a...a3d71d8

2026-10-16 [email protected] 0 Initialize variables in the DlComplexityPathReceiver helper (flutter/flutter#194540)
2026-10-16 [email protected] Roll Fuchsia Test Scripts from V-qQL4158Nw6L-QLi... to 2T18KC7-LDQtmQiLP... (flutter/flutter#194527)
2026-10-16 [email protected] Remove BorderMaskBlur (flutter/flutter#194508)
2026-10-16 [email protected] Roll Skia from a577264dc087 to 470b8816a261 (1 revision) (flutter/flutter#194526)
2026-10-16 [email protected] [ios] Use DFS traversal to find engines of a scene automatically (rather than manually manage the engines list) (flutter/flutter#192457)
2026-10-16 [email protected] [Skia][DisplayList] Map local bounds through the local matrix in DlLocalMatrixImageFilter (flutter/flutter#193963)
2026-10-16 [email protected] [framework] Fix avoid_setters_without_getters warnings on mixin declarations  (flutter/flutter#193856)
2026-10-16 [email protected] Roll Dart SDK from 84686ea04815 to 55a734a01fb1 (3 revisions) (flutter/flutter#194522)
2026-10-16 [email protected] Roll Skia from d131b7789482 to a577264dc087 (7 revisions) (flutter/flutter#194514)
2026-10-16 [email protected] Roll Fuchsia GN SDK from PDJz3b3CL31uoLOkD... to kFdyRnojbVy0cxvRZ... (flutter/flutter#194510)
2026-10-16 [email protected] Roll GN to f9eca75ae554 (flutter/flutter#194471)
2026-10-16 [email protected] refactor(flutter_tools): migrate RunCommand and RunCommandBase to OptionDescriptor and OptionBundle (flutter/flutter#194362)
2026-10-15 [email protected] refactor(flutter_tools): finish TestCommand OptionDescriptor and OptionBundle migration (flutter/flutter#194361)
2026-10-15 [email protected] [iOS][a11y] Fix VoiceOver announcement for unfocused password fields (flutter/flutter#192851)
2026-10-15 [email protected] Roll pub packages (flutter/flutter#194477)
2026-10-15 [email protected] refactor(flutter_tools): finish DriveCommand OptionDescriptor migration (flutter/flutter#194360)
2026-10-15 [email protected] [flutter_tools] Roll package:vm_service to 15.4.0 (flutter/flutter#194467)
2026-10-15 [email protected] refactor(flutter_tools): migrate AttachCommand to OptionDescriptor and OptionBundle (flutter/flutter#194359)
2026-10-15 [email protected] [flutter_tools] Inject dependencies into update-packages and package_map (flutter/flutter#194046)
2026-10-15 [email protected] [impeller] parallelize opengles shader compiles (flutter/flutter#194022)
2026-10-15 [email protected] Roll pub packages (flutter/flutter#194469)
2026-10-15 [email protected] Restrict web dev server to loopback by default (flutter/flutter#187595)
2026-10-15 [email protected] Roll Packages from a1ed23c to da40eaf (33 revisions) (flutter/flutter#194459)
2026-10-15 [email protected] [web] Render soft hyphens on Skwasm (flutter/flutter#193850)
2026-10-15 [email protected] refactor(flutter_tools): migrate AssembleCommand to OptionDescriptor (flutter/flutter#194358)
2026-10-15 [email protected] [windows] Add OpenGL golden test against ANGLE at D3D11 feature level 10_0 (flutter/flutter#194321)

If this roll has caused a breakage, revert this CL and set the roller
to dry run mode using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…her than manually manage the engines list) (flutter#192457)

This PR simplifies iOS embedder's UIScene lifecycle logic. The key idea
is to perform DFS traversal to find engines to send scene events to
(rather than manually manage the `flutterManagedEngines` list).

By doing so, we are able to remove the need to manual registering
engines (mostly, see below) and other logics too.

This PR also fixes a few bugs along the way for both single and
multi-scenes.

### Simplifications: 
[multi-scene] Use DFS to find engines of a scene automatically (rather
than manually manage `flutterManagedEngines` list)
[multi-scene] Manual register/unregister is only required for cases
where FlutterVC is not added to the scene but still wanna receive events
(e.g. headless/add-to-app) (Fixes
flutter#185689)
[single-scene] Remove NSNotification code path for connection events

### Bug fixes
[single-scene] Fixes missing connection event for an edge case of
implicit engine usage (Fixes
flutter#186547)
[single-scene] Fix missing lifecycle events if the engine is not
attached to any scenes (e.g. headless engines/add-to-app) (Fixes
flutter#191907)
[multi-scene] connectionOptions should be per scene (Fixes
flutter#191039)
[multi-scene] Manual register/unregister API is now registered per scene
rather than per scene delegate (added `scene` parameter)
[multi-scene] Scene lifecycle listeners are not per-scene rather than
per scene delegate (Fixes
flutter#185687)

Also fixes flutter#185688 
Also fixes flutter#185702
Also fixes flutter#192344


*If you had to change anything in the [flutter/tests] repo, include a
link to the migration guide as per the [breaking change policy].*

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [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 in-code documentation (doc comments with
`///`).
- [ ] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [ ] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [ ] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

If this change needs to override an active code freeze, provide a
comment explaining why. The code freeze workflow can be overridden by
code reviewers. See pinned issues for any active code freezes with
guidance.

**Note**: The Flutter team is currently trialing the use of [Gemini Code
Assist for
GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code).
Comments from the `gemini-code-assist` bot should not be taken as
authoritative feedback from the Flutter team. If you find its comments
useful you can update your code accordingly, but if you are unsure or
disagree with the feedback, please feel free to wait for a Flutter team
member's review for guidance on which automated comments should be
addressed.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment