Skip to content

UIScene Migration documentation updates#13045

Merged
sfshaza2 merged 6 commits intoflutter:mainfrom
LouiseHsu:uiscenedelegate-updates
Feb 11, 2026
Merged

UIScene Migration documentation updates#13045
sfshaza2 merged 6 commits intoflutter:mainfrom
LouiseHsu:uiscenedelegate-updates

Conversation

@LouiseHsu
Copy link
Contributor

Update UIScene migration guidelines.

Presubmit checklist

  • If you are unwilling, or unable, to sign the CLA, even for a tiny, one-word PR, please file an issue instead of a PR.
  • If this PR is not meant to land until a future stable release, mark it as draft with an explanation.
  • This PR follows the Google Developer Documentation Style Guidelines—for example, it doesn't use i.e. or e.g., and it avoids I and we (first-person pronouns).
  • This PR uses semantic line breaks
    of 80 characters or fewer.

@LouiseHsu LouiseHsu requested a review from vashworth February 6, 2026 23:35
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @LouiseHsu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request updates the UIScene migration guidelines documentation. It provides clear instructions and code examples for developers transitioning from UIApplicationDelegate to UISceneDelegate methods, and crucially, outlines how to adapt Flutter plugins to support multi-scene environments by addressing deprecated UIApplication APIs. The update aims to simplify the migration process and ensure future compatibility with iOS multi-scene features.

Highlights

  • UISceneDelegate Method Mapping: Added a comprehensive table mapping common UIApplicationDelegate methods to their respective UISceneDelegate equivalents, providing a clear guide for migration.
  • Multi-Scene API Migration: Introduced a new section detailing the migration of deprecated UIApplication APIs (like mainScreen, keyWindow, windows) to UIScene equivalents to enable multi-scene support.
  • Flutter Plugin Registrar Handling: Provided Objective-C and Swift code examples demonstrating how to create a FlutterViewProvider and integrate it into a plugin's registration process, allowing the pluginRegistrar to be managed as a variable for multi-scene compatibility.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/content/release/breaking-changes/uiscenedelegate.md
    • Expanded the documentation with a table mapping UIApplicationDelegate methods to their UISceneDelegate counterparts.
    • Added a new section detailing the migration of deprecated UIApplication APIs (UIScreen mainScreen, UIApplication keyWindow, UIApplication windows, UIApplicationDelegate window) to UIScene equivalents for multi-scene support.
    • Included Objective-C and Swift code examples for implementing a FlutterViewProvider to manage FlutterPluginRegistrar as a variable, facilitating multi-scene compatibility in plugins.
Activity
  • No human activity (comments, reviews, etc.) has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

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

Choose a reason for hiding this comment

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

Code Review

The pull request updates the UIScene migration guidelines, adding a new table for App Delegate methods and their Scene Delegate equivalents, and providing guidance on migrating deprecated APIs for multi-scene support. The changes improve the clarity and completeness of the migration documentation for Flutter iOS developers.

| [`applicationDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive) | [`sceneDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197915-scenedidbecomeactive) |
| [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) |
| [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) |
| [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) |
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The applicationDidEnterBackground method's Scene Delegate Equivalent has a colon at the end (sceneDidEnterBackground:). This is inconsistent with the other entries in the table and should be removed for clarity and uniformity.

Suggested change
| [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) |
| [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) |

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Gemini, make the colons consistent

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
- YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] init];
+ YourFlutterViewProvider *viewProvider = [[YourFlutterViewProvider alloc] initWithRegistrar:registrar];
+. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider];
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

There is an extra period at the beginning of this line. This appears to be a typo and should be removed.

Suggested change
+. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider];
YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider];

@LouiseHsu LouiseHsu changed the title init UIScene Migration documentation updates Feb 6, 2026
Copy link
Contributor

@sfshaza2 sfshaza2 left a comment

Choose a reason for hiding this comment

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

@LouiseHsu, lgtm, but are you ok with the. minor changes that the bot is requesting? I leave them to you.

@flutter-website-bot
Copy link
Collaborator

flutter-website-bot commented Feb 7, 2026

Visit the preview URL for this PR (updated for commit 6afaaae):

https://flutter-docs-prod--pr13045-uiscenedelegate-updates-ubc3v0i3.web.app

| [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` |
| [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
| [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` |
| `UIApplicationDelegate window` | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
| `UIApplicationDelegate window` | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
| [`UIApplicationDelegate window`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/window) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |

| [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) |
| [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) |
| [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) |
| [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) | [`scene:continueUserActivity:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3238053-scene) |
Copy link
Contributor

Choose a reason for hiding this comment

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

The link for scene is not working on this one

| [`applicationDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive) | [`sceneDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197915-scenedidbecomeactive) |
| [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) |
| [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) |
| [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) |
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with Gemini, make the colons consistent

</Tab>
</Tabs>

Optionally, while the below methods are not deprecated by UIScene, they should be migrated if you would like to support multiscene in the future. Note that this also entails an extra step of making your pluginRegistrar a variable.
Copy link
Contributor

Choose a reason for hiding this comment

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

I would move this section below step 5 and make it step 6

Perhaps:

6. [Optional] Migrate other deprecated APIs to support multiple scenes in the future.

@LouiseHsu LouiseHsu marked this pull request as ready for review February 9, 2026 22:25
@LouiseHsu LouiseHsu requested a review from a team as a code owner February 9, 2026 22:25
Comment on lines 814 to 880
First, create a new view provider with an instance method that holds a reference to your `FlutterPluginRegistrar`.

<Tabs key="ios-language-switcher">
<Tab name="Objective-C">

```objc
@implementation YourFlutterViewProvider
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
self = [super init];
if (self) {
_registrar = registrar;
}
return self;
}

- (UIViewController *)viewController {
return self.registrar.viewController;
}
@end
```

</Tab>
<Tab name="Swift">

```swift
final class ViewPresenterProvider: ViewPresenterProvider {
private let registrar: FlutterPluginRegistrar

init(registrar: FlutterPluginRegistrar) {
self.registrar = registrar
}

var viewPresenter: ViewPresenter? {
registrar.viewController
}
}
```
</Tab>
</Tabs>

Next, in your plugin’s `registerWithRegistrar` method, initialize your new view provider and pass it into your plugin instance.

<Tabs key="ios-language-switcher">
<Tab name="Objective-C">

```objc diff
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
- YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] init];
+ YourFlutterViewProvider *viewProvider = [[YourFlutterViewProvider alloc] initWithRegistrar:registrar];
+. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider];
SetUpFLTImagePickerApi(registrar.messenger, instance);
}
```

</Tab>
<Tab name="Swift">

```swift diff
public static func register(with registrar: FlutterPluginRegistrar) {
- let instance = FileSelectorPlugin()
+ let instance = YourFlutterPlugin(
viewPresenterProvider: ViewPresenterProvider(registrar: registrar))
YourFlutterPluginApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance)
}
```
</Tab>
</Tabs>
Copy link
Contributor

Choose a reason for hiding this comment

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

I think let's simplify this. Wrapping it in a ViewProvider is likely too verbose for an average developer.

I'd say something like:

Instead of accessing these APIs, you can access the windowScene through the viewController. See examples below.

ObjC

@interface MyPlugin ()
+ @property(nonatomic, weak) NSObject<FlutterPluginRegistrar> *registrar;

+ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar;
@end

@implementation MyPlugin

+  - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
+    self = [super init];
+    if (self) {
+      _registrar = registrar;
+    }
+    return self;
+  }

  + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
-    MyPlugin *instance = [[MyPlugin alloc] init];
+    MyPlugin *instance = [[MyPlugin alloc] initWithRegistrar:registrar];
  }

  - (void)someMethod {
-    UIScreen *screen = [UIScreen mainScreen];
+    UIScreen *screen = self.registrar.viewController.view.window.windowScene.screen;

-    UIWindow *window = [UIApplication sharedApplication].delegate.window;
+    UIWindow *window = self.registrar.viewController.view.window;

-    UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
+    if (@available(iOS 15.0, *)) {
+      UIWindow *keyWindow = self.registrar.viewController.view.window.windowScene.keyWindow;
+    } else {
+      for (UIWindow *window in self.registrar.viewController.view.window.windowScene.windows) {
+        if (window.isKeyWindow) {
+          UIWindow *keyWindow = window;
+        }
+      }
+    }

-    NSArray<UIWindow *> *windows = [UIApplication sharedApplication].windows;
+    NSArray<UIWindow *> *windows = self.pluginRegistrar.viewController.view.window.windowScene.windows;
  }

Swift

public class MyPlugin: NSObject, FlutterPlugin {
+  var registrar: FlutterPluginRegistrar

+  init(registrar: FlutterPluginRegistrar) {
+    self.registrar = registrar
+  }

  public static func register(with registrar: FlutterPluginRegistrar) {
-    let instance = MyPlugin()
+    let instance = MyPlugin(registrar: registrar)
  }

  func someMethod {
-    let screen = UIScreen.main;
+    let screen = self.registrar.viewController?.view.window?.windowScene?.screen;

-    let window = UIApplication.shared.delegate?.window;
+    let window = self.registrar.viewController?.view.window;

-    let keyWindow = UIApplication.shared.keyWindow;
+    if #available(iOS 15.0, *) {
+      let keyWindow = self.registrar.viewController?.view.window?.windowScene?.keyWindow
+    } else {
+      let keyWindow = self.registrar.viewController?.view.window?.windowScene?.windows.filter({ $0.isKeyWindow }).first
+    }
   
-    let windows = UIApplication.shared.windows;
+    let windows = self.registrar.viewController?.view.window?.windowScene?.windows;

  }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah i see, okay.

Comment on lines 809 to 812
| [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` |
| [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
| [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` |
| [`UIApplicationDelegate window`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/window) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
Copy link
Contributor

Choose a reason for hiding this comment

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

|:---------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------|
| [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` |
| [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
| [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` |
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

@sfshaza2 sfshaza2 left a comment

Choose a reason for hiding this comment

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

lgtm

@sfshaza2 sfshaza2 merged commit d8357f3 into flutter:main Feb 11, 2026
10 checks passed
sfshaza2 pushed a commit that referenced this pull request Feb 17, 2026
follow up to #13045

## Presubmit checklist

- [ ] If you are unwilling, or unable, to sign the CLA, even for a
_tiny_, one-word PR, please file an issue instead of a PR.
- [ ] If this PR is not meant to land until a future stable release,
mark it as draft with an explanation.
- [x] This PR follows the [Google Developer Documentation Style
Guidelines](https://developers.google.com/style)—for example, it doesn't
use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first-person
pronouns).
- [x] This PR uses [semantic line
breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks)
  of 80 characters or fewer.

---------

Co-authored-by: Victoria Ashworth <15619084+vashworth@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments