-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Add scene plugin lifecycle events #175866
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add scene plugin lifecycle events #175866
Conversation
| "framework/Headers/FlutterEngineGroup.h", | ||
| "framework/Headers/FlutterHeadlessDartRunner.h", | ||
| "framework/Headers/FlutterPlatformViews.h", | ||
| "framework/Headers/FlutterPlugin.h", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All changes above this point are for the integration test
|
/gemini review |
There was a problem hiding this comment.
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 introduces support for UIScene lifecycle events in Flutter plugins. It adds new public APIs (FlutterSceneLifeCycleDelegate, FlutterSceneLifeCycleProvider, FlutterPluginSceneLifeCycleDelegate) to allow plugins to receive scene-specific lifecycle events. The changes include a fallback mechanism to forward application-level lifecycle events to plugins that have not yet migrated to the new scene-based APIs. The implementation is well-structured, separating concerns between scene and application lifecycle delegates. A comprehensive integration test suite is also added to verify the behavior across various migration scenarios. My review focuses on the correctness and robustness of the new event forwarding logic.
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Show resolved
Hide resolved
stuartmorgan-g
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All my comments are minor, and almost all optional, so I don't think this needs another review from me, but let me know if you want me to take another look at anything you change.
The integration testing here is really, really nice!
| const String moduleName = 'my_module'; | ||
| await flutter( | ||
| 'create', | ||
| options: <String>['--org', 'io.flutter.devicelab', '--template=module', moduleName], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do flutter/flutter tests still use io.flutter rather than dev.flutter? For flutter/plugins we phased out as much use of io.flutter as possible.
(Doesn't really matter, just curious.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we do. I'll swap it. Do you know why we replaced it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No clue; I assume it was a marketing decision. (We still have flutter.io, but it redirects to flutter.dev).
dev/integration_tests/ios_add2app_uiscene/flutterapp/pubspec-LifeCycleTest.yaml
Outdated
Show resolved
Hide resolved
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Outdated
Show resolved
Hide resolved
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Outdated
Show resolved
Hide resolved
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifecycle.mm
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifecycle.mm
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterSceneLifeCycle.h
Show resolved
Hide resolved
|
|
||
| /** | ||
| * Protocol for listener of events from the UIWindowSceneDelegate, typically a FlutterPlugin. | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is FLUTTER_DARWIN_EXPORT needed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tbh, I don't really understand what does and doesn't need FLUTTER_DARWIN_EXPORT. My integration tests worked without it.
engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterSceneLifeCycle.h
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterSceneLifeCycle.h
Show resolved
Hide resolved
| id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate]; | ||
| - (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate { | ||
| // If the plugin conforms to FlutterSceneLifeCycleDelegate, add it to the engine. | ||
| if ([delegate conformsToProtocol:@protocol(FlutterSceneLifeCycleDelegate)]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're expecting FlutterPlugin to conform to FlutterSceneLifeCycleDelegate, but it doesn't seem to be documented or implied in the interface (also I think even with documentation that says FlutterPlugin also needs to conform to FlutterSceneLifecycleDelegate, it will be a bit difficult to discover).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be possible to add a separate method to the registrar for UISceneDelegate registration instead of reusing the one for UIAppDelegate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the doc comment for FlutterSceneLifeCycleDelegate. I also plan to write website documentation. We could definitely separate this into 2 methods. Do you think that would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm leaning towards having a separate method for scene event registration since people don't always read docs (and the existing method is called addApplicationDelegate:).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, I was on the fence about it. I added it and updated the integration test
| /** | ||
| * Protocol for listener of events from the UIWindowSceneDelegate, typically a FlutterPlugin. | ||
| */ | ||
| @protocol FlutterSceneLifeCycleDelegate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: looks like we use "lifecycle" as a single word in most places? e.g.,
1: https://main-api.flutter.dev/flutter/dart-ui/AppLifecycleState.html,
2: https://api.flutter.dev/macos-embedder/category_flutter_app_delegate_07_08.html#a926aab76a90dbc4e9fba5033ac694183
The only place with this spelling I found is here: https://api.flutter.dev/ios-embedder/category_flutter_app_delegate_07_08.html#ab86a53588ed62c71760a9c5d52da18ff
It's already inconsistent but I suppose we should stick with the most prevalent spelling?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So there are already three public APIs with the 2-word spelling: FlutterApplicationLifeCycleDelegate, FlutterAppLifeCycleProvider, FlutterPluginAppLifeCycleDelegate. And the APIs added here are like the scene-equivalents to those, which is why I went this this spelling in the public headers. It also bums me out that it's different all over the place 🙈
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Outdated
Show resolved
Hide resolved
.../src/flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
Outdated
Show resolved
Hide resolved
| // The fallback is unnecessary if the plugin conforms to FlutterSceneLifeCycleDelegate. | ||
| // This means that the plugin has migrated to scene lifecycle events and shouldn't require | ||
| // application events. However, the plugin may still have the application event implemented to | ||
| // maintain compatibility with un-migrated apps, which is why the fallback should be checked |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty sad that now plugin authors will have to maintain two different implementations for migrated and unmigrated apps. I'm curious whether UIKit also posts a UISceneDidEnterBackgroundNotification for unmigrated apps so plugin authors don't have worry about unmigrated apps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are scene notifications for like the basic events like active, disconnect, foreground, background, etc. However, there are not notifications for events like scene: openURLContexts, scene:continueUserActivity, etc. I personally think it's be more confusing if plugin authors only need to support both for specific ones rather than all of them.
Perhaps this is something we can come back to if we have time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All unmigrated apps are single-scene apps (I think?) so I suppose we can get the scene from UIApplication.connectedScenes (not sure what's supposed to happen in shared extensions tho). Yeah we can come back to this later.
engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifeCycle.mm
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneDelegateTest.m
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterSceneLifecycle.h
Outdated
Show resolved
Hide resolved
engine/src/flutter/shell/platform/darwin/ios/framework/Headers/FlutterSceneLifeCycle.h
Show resolved
Hide resolved
LongCatIsLooong
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM in general modulo comments regarding skipping incoming URLs.
Additionally the applicationLifeCycleDelegate implementation where we have to guess to get access to the FlutterPluginAppDelegateLifeCycle instance to send the fallback events seems to be making too much assumptions but we can address this later.
| /** | ||
| * Called if this has been registered for `UIWindowScene` callbacks. | ||
| * | ||
| * @return `YES` if this handles the request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm what if my plugin only handles a subset of the incoming URLContexts? I suppose the BOOL indicates any URL instead of all URLs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated comment to say any. It's not clear in what situation UIKit would send more than one url, but I looked at other usages in g3 and Github and people are just using the first that succeeds.
engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm
Outdated
Show resolved
Hide resolved
|
|
||
| - (BOOL)sceneFallbackOpenURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts { | ||
| for (UIOpenURLContext* context in URLContexts) { | ||
| return [self application:FlutterSharedApplication.application |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can't return early here? Or some URLs are going to be left unprocessed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I don't understand what we're supposed to return here, does the boolean flag mean all incoming URLs are processed or any URL?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops this was my bad, thank you for being such a great reviewer and catching 🙏 Yeah the bool means any url was processed.
I updated it. It now returns whichever succeeds first.
| consumedByPlugin = YES; | ||
| } | ||
| } | ||
| if (!consumedByPlugin) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm understanding correctly this is an example of converting a scene event to an app event, for unmigrated plugins. But it looks like the implementation is biased towards migrated plugins, as in if any of the migrated plugins returned YES the fallback won't happen (but all migrated plugins will get a chance to handle the event no matter what other plugins responded to the openURL request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Basically only one plugin per engine should get to consume the event.
if any of the migrated plugins returned YES the fallback won't happen
Correct
but all migrated plugins will get a chance to handle the event no matter what other plugins responded to the openURL request
Nope, each FlutterEngine will get a change to handle the event with its plugins, but only one plugin per engine can handle the event. See below:
flutter/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterSceneLifeCycle.mm
Lines 316 to 326 in a640ddb
| - (BOOL)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts { | |
| for (NSObject<FlutterSceneLifeCycleDelegate>* delegate in _delegates.allObjects) { | |
| if ([delegate respondsToSelector:_cmd]) { | |
| if ([delegate scene:scene openURLContexts:URLContexts]) { | |
| // Only allow one plugin to process this event. | |
| return YES; | |
| } | |
| } | |
| } | |
| return NO; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is how it's handled for applications, which is what I'm mimicking:
Lines 318 to 332 in f3ea7a1
| - (BOOL)application:(UIApplication*)application | |
| openURL:(NSURL*)url | |
| options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options { | |
| for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) { | |
| if (!delegate) { | |
| continue; | |
| } | |
| if ([delegate respondsToSelector:_cmd]) { | |
| if ([delegate application:application openURL:url options:options]) { | |
| return YES; | |
| } | |
| } | |
| } | |
| return NO; | |
| } |
I'll fix in my next PR so I don't have to wait for testing again lol |
…10145) Manual roll requested by [email protected] flutter/flutter@96fe3b3...c9608e2 2025-09-30 [email protected] Implement framework interface for the dialog window archetype (flutter/flutter#176202) 2025-09-30 [email protected] Update flutter test to use SemanticsFlags (flutter/flutter#175987) 2025-09-30 [email protected] Set minimum supported java version to 17 (flutter/flutter#176226) 2025-09-30 [email protected] Reduce timeout for Linux web_tool_tests back to 60 (flutter/flutter#176286) 2025-09-30 [email protected] Roll Packages from 34eec78 to 287739d (9 revisions) (flutter/flutter#176284) 2025-09-30 [email protected] [web] Bump Firefox to 143.0 (flutter/flutter#176110) 2025-09-30 [email protected] Migrate to `WidgetStateBorderSide` (flutter/flutter#176164) 2025-09-30 [email protected] Enhance input decorator padding logic for character counter in text f… (flutter/flutter#175706) 2025-09-30 [email protected] Update the test package for the web engine unit test bits. (flutter/flutter#176241) 2025-09-30 [email protected] Warn if embedder API calls don't return success (flutter/flutter#176184) 2025-09-30 [email protected] Roll Fuchsia Test Scripts from APSBP-sS-3FX69Ihf... to JUeFbA8y0E-_pj-bg... (flutter/flutter#176243) 2025-09-30 [email protected] Roll GN to 81b24e01 (flutter/flutter#176119) 2025-09-29 [email protected] Rename DisplayMonitor to DisplayManager on Win32 (flutter/flutter#175619) 2025-09-29 [email protected] [Android] Use headingLevel for heading accessibility property (flutter/flutter#175416) 2025-09-29 [email protected] BUILD.gn: Support LTO build on Linux (flutter/flutter#176191) 2025-09-29 [email protected] fix `assertEquals` arguments are in wrong order in `FlutterJNITest.java` (flutter/flutter#175728) 2025-09-29 [email protected] Add tests for `Project` getters (flutter/flutter#175994) 2025-09-29 [email protected] Roll Fuchsia Linux SDK from 8zjcJic_DtvB2Bo2x... to rcOl0yxJb4znJ903Y... (flutter/flutter#176215) 2025-09-29 [email protected] Clean up typos in `PlatformViewsControllerTest.java` (flutter/flutter#175725) 2025-09-29 [email protected] Migrate java 11 usage to java 17 usage for templates (flutter/flutter#176203) 2025-09-29 [email protected] User Invoke-Expression instead of call operator for nested Powershell scripts invocations (on Windows) (flutter/flutter#175941) 2025-09-29 [email protected] Update changelog as on 3.35 branch (flutter/flutter#176216) 2025-09-29 [email protected] fix typo in `Crashes.md` (flutter/flutter#175959) 2025-09-29 [email protected] Add scene plugin lifecycle events (flutter/flutter#175866) 2025-09-29 [email protected] Migrate tests and documentation to set java version to 17 (flutter/flutter#176204) 2025-09-29 [email protected] Update Engine CI to use NDK r28c (flutter/flutter#175870) If this roll has caused a breakage, revert this CL and stop the roller 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
This PR forwards scene lifecycle events from the SceneDelegate to
registered plugins.
This PR introduced 3 new public APIs:
* `FlutterSceneLifeCycleDelegate` (protocol) - This is the protocol that
plugins conform to
* `FlutterSceneLifeCycleProvider` (protocol) - This is the protocol that
a SceneDelegate can conform to instead of `FlutterSceneDelegate`
* `FlutterPluginSceneLifeCycleDelegate` (interface) - This is the class
object that a SceneDelegate using `FlutterSceneLifeCycleProvider` can
use to forward scene events to
The flow of events are as followed when SceneDelegate subclasses the
`FlutterSceneDelegate`:
* `FlutterSceneDelegate` receives `sceneDidBecomeActive` event from
UIKit
* `FlutterSceneDelegate` forwards `sceneDidBecomeActive` to
`FlutterPluginSceneLifeCycleDelegate`
* `FlutterPluginSceneLifeCycleDelegate` loops through each
`FlutterEngine` it has and forwards `sceneDidBecomeActive` to the
engine's `FlutterEnginePluginSceneLifeCycleDelegate`
* `FlutterEnginePluginSceneLifeCycleDelegate` loops through each plugin
that conforms to `FlutterSceneLifeCycleDelegate` it has and forwards
`sceneDidBecomeActive` to the plugin
* `FlutterPluginSceneLifeCycleDelegate` forwards `sceneDidBecomeActive`
to `FlutterPluginAppLifeCycleDelegate` using
`sceneDidBecomeActiveFallback`
* `FlutterPluginAppLifeCycleDelegate` loops through all plugins and
calls `applicationDidBecomeActive` if the plugin does not conform to
`FlutterSceneLifeCycleDelegate`
When using `FlutterSceneLifeCycleProvider` instead of
`FlutterSceneDelegate`, scene events are manually forwarded to
`FlutterPluginSceneLifeCycleDelegate` by the iOS developer. For example:
```swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate, FlutterSceneLifeCycleProvider {
var sceneLifeCycleDelegate: FlutterPluginSceneLifeCycleDelegate = FlutterPluginSceneLifeCycleDelegate()
func sceneDidBecomeActive(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneDidBecomeActive(scene)
}
```
Fixes flutter#174398.
## 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.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
**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
[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
[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
This PR forwards scene lifecycle events from the SceneDelegate to
registered plugins.
This PR introduced 3 new public APIs:
* `FlutterSceneLifeCycleDelegate` (protocol) - This is the protocol that
plugins conform to
* `FlutterSceneLifeCycleProvider` (protocol) - This is the protocol that
a SceneDelegate can conform to instead of `FlutterSceneDelegate`
* `FlutterPluginSceneLifeCycleDelegate` (interface) - This is the class
object that a SceneDelegate using `FlutterSceneLifeCycleProvider` can
use to forward scene events to
The flow of events are as followed when SceneDelegate subclasses the
`FlutterSceneDelegate`:
* `FlutterSceneDelegate` receives `sceneDidBecomeActive` event from
UIKit
* `FlutterSceneDelegate` forwards `sceneDidBecomeActive` to
`FlutterPluginSceneLifeCycleDelegate`
* `FlutterPluginSceneLifeCycleDelegate` loops through each
`FlutterEngine` it has and forwards `sceneDidBecomeActive` to the
engine's `FlutterEnginePluginSceneLifeCycleDelegate`
* `FlutterEnginePluginSceneLifeCycleDelegate` loops through each plugin
that conforms to `FlutterSceneLifeCycleDelegate` it has and forwards
`sceneDidBecomeActive` to the plugin
* `FlutterPluginSceneLifeCycleDelegate` forwards `sceneDidBecomeActive`
to `FlutterPluginAppLifeCycleDelegate` using
`sceneDidBecomeActiveFallback`
* `FlutterPluginAppLifeCycleDelegate` loops through all plugins and
calls `applicationDidBecomeActive` if the plugin does not conform to
`FlutterSceneLifeCycleDelegate`
When using `FlutterSceneLifeCycleProvider` instead of
`FlutterSceneDelegate`, scene events are manually forwarded to
`FlutterPluginSceneLifeCycleDelegate` by the iOS developer. For example:
```swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate, FlutterSceneLifeCycleProvider {
var sceneLifeCycleDelegate: FlutterPluginSceneLifeCycleDelegate = FlutterPluginSceneLifeCycleDelegate()
func sceneDidBecomeActive(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneDidBecomeActive(scene)
}
```
Fixes flutter#174398.
## 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.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
**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
[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
[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

This PR forwards scene lifecycle events from the SceneDelegate to registered plugins.
This PR introduced 3 new public APIs:
FlutterSceneLifeCycleDelegate(protocol) - This is the protocol that plugins conform toFlutterSceneLifeCycleProvider(protocol) - This is the protocol that a SceneDelegate can conform to instead ofFlutterSceneDelegateFlutterPluginSceneLifeCycleDelegate(interface) - This is the class object that a SceneDelegate usingFlutterSceneLifeCycleProvidercan use to forward scene events toThe flow of events are as followed when SceneDelegate subclasses the
FlutterSceneDelegate:FlutterSceneDelegatereceivessceneDidBecomeActiveevent from UIKitFlutterSceneDelegateforwardssceneDidBecomeActivetoFlutterPluginSceneLifeCycleDelegateFlutterPluginSceneLifeCycleDelegateloops through eachFlutterEngineit has and forwardssceneDidBecomeActiveto the engine'sFlutterEnginePluginSceneLifeCycleDelegateFlutterEnginePluginSceneLifeCycleDelegateloops through each plugin that conforms toFlutterSceneLifeCycleDelegateit has and forwardssceneDidBecomeActiveto the pluginFlutterPluginSceneLifeCycleDelegateforwardssceneDidBecomeActivetoFlutterPluginAppLifeCycleDelegateusingsceneDidBecomeActiveFallbackFlutterPluginAppLifeCycleDelegateloops through all plugins and callsapplicationDidBecomeActiveif the plugin does not conform toFlutterSceneLifeCycleDelegateWhen using
FlutterSceneLifeCycleProviderinstead ofFlutterSceneDelegate, scene events are manually forwarded toFlutterPluginSceneLifeCycleDelegateby the iOS developer. For example:Fixes #174398.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot 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.