Adopción de UISceneDelegate
Aprende cómo migrar tu app iOS de Flutter, integración add-to-app, o plugin al ciclo de vida UIScene requerido por Apple usando FlutterSceneDelegate.
Resumen
#Apple ahora requiere que los desarrolladores de iOS adopten el ciclo de vida UIScene.
Esta migración tiene implicaciones para la
secuencia de inicio de la aplicación
y el ciclo de vida de la aplicación.
Contexto
#Durante la WWDC25, Apple anunció lo siguiente:
En la versión posterior a iOS 26, cualquier aplicación UIKit construida con el último SDK deberá usar el ciclo de vida UIScene, de lo contrario no se iniciará.
Para adoptar el ciclo de vida UIScene,
sigue la guía que corresponda a tu caso de uso:
- Para todas las aplicaciones de Flutter que soportan iOS, sigue la guía de migración para aplicaciones de Flutter.
- Para aplicaciones de Flutter embebidas en aplicaciones iOS nativas existentes, sigue la guía de migración para add-to-app.
- Para plugins de Flutter que utilicen eventos del ciclo de vida de la aplicación iOS, sigue la guía de migración para plugins de Flutter.
Migrar a UIScene desplaza la función del AppDelegate:
el UISceneDelegate ahora maneja el ciclo de vida de la interfaz de usuario,
mientras que el AppDelegate sigue siendo responsable de
los eventos del proceso y del ciclo de vida general de la aplicación.
Mueve toda la lógica relacionada con la UI del AppDelegate a los
métodos correspondientes del UISceneDelegate.
Después de migrar a UIScene, UIKit ya no
llamará a los métodos del AppDelegate relacionados con el estado de la UI.
Migrar una aplicación de Flutter
#Auto-migrar
#A partir de Flutter 3.41, UIScene está soportado de forma predeterminada.
Si tu AppDelegate no ha sido personalizado,
la CLI de Flutter migra automáticamente tu aplicación.
Para iniciar la migración, compila o ejecuta tu aplicación con
los comandos flutter run o flutter build ios.
Si la migración tiene éxito,
la CLI mostrará el mensaje Finished migration to UIScene lifecycle y
no se requerirá ninguna acción adicional.
De lo contrario, la CLI te advertirá y
te proporcionará instrucciones para migrar manualmente.
Migrar AppDelegate
#Anteriormente, los plugins de Flutter se registraban en
application:didFinishLaunchingWithOptions:.
Para adaptarse a la nueva secuencia de inicio de la aplicación,
ahora debes registrar los plugins en un
nuevo callback didInitializeImplicitFlutterEngine.
-
Haz que tu
AppDelegatecumpla con el protocoloFlutterImplicitEngineDelegatey mueve el registro deGeneratedPluginRegistrantadidInitializeImplicitFlutterEngine.my_app/ios/Runner/AppDelegate.swiftswift@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) } }my_app/ios/Runner/AppDelegate.hobjc@interface AppDelegate : FlutterAppDelegate @interface AppDelegate : FlutterAppDelegate <FlutterImplicitEngineDelegate>my_app/ios/Runner/AppDelegate.mobjc- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [GeneratedPluginRegistrant registerWithRegistry:self]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } - (void)didInitializeImplicitFlutterEngine:(NSObject<FlutterImplicitEngineBridge>*)engineBridge { [GeneratedPluginRegistrant registerWithRegistry:engineBridge.pluginRegistry]; } -
Si aplica, crea los method channels y platform views en
didInitializeImplicitFlutterEngine.Si anteriormente creaste method channels o platform views en
application:didFinishLaunchingWithOptions:, mueve esa lógica adidInitializeImplicitFlutterEngine.swiftfunc didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { // Register plugins with `engineBridge.pluginRegistry`: GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) // Create method channels with `engineBridge.applicationRegistrar.messenger()`: let batteryChannel = FlutterMethodChannel( name: "samples.flutter.dev/battery", binaryMessenger: engineBridge.applicationRegistrar.messenger() ) // Create platform views with `engineBridge.applicationRegistrar.messenger()`: let factory = FLNativeViewFactory(messenger: engineBridge.applicationRegistrar.messenger()) }objc- (void)didInitializeImplicitFlutterEngine:(NSObject<FlutterImplicitEngineBridge>*)engineBridge { // Register plugins with `engineBridge.pluginRegistry`: [GeneratedPluginRegistrant registerWithRegistry:engineBridge.pluginRegistry]; // Create method channels with `engineBridge.applicationRegistrar.messenger`: FlutterMethodChannel* batteryChannel = [FlutterMethodChannel methodChannelWithName:@"samples.flutter.dev/battery" binaryMessenger:engineBridge.applicationRegistrar.messenger]; // Create platform views with `engineBridge.applicationRegistrar.messenger`: FLNativeViewFactory* factory = [[FLNativeViewFactory alloc] initWithMessenger:engineBridge.applicationRegistrar.messenger]; } -
Migrar cualquier lógica personalizada dentro de los eventos del ciclo de vida de la aplicación.
Apple ha deprecado los eventos del ciclo de vida de la aplicación relacionados con el estado de la UI. Después de migrar al ciclo de vida
UIScene, UIKit ya no llama a estos eventos.Si utilizaste una de estas APIs deprecadas, como
applicationDidBecomeActive, probablemente necesites crear unSceneDelegatey migrar a los eventos del ciclo de vida de la escena. Para saber más, consulta la documentación de Apple sobre migración.Si implementas tu propio
SceneDelegate, debes heredar deFlutterSceneDelegateo cumplir con el protocoloFlutterSceneLifeCycleProvider. Para ver ejemplos, consulta Crear o actualizar un SceneDelegate.
Migrar Info.plist
#Para completar la migración al ciclo de vida de UIScene,
añade una entrada de Application Scene Manifest a tu Info.plist.
Como se muestra en el editor de Xcode:
Como XML:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>
Crear un SceneDelegate (opcional)
#Si necesitas acceso al SceneDelegate,
puedes crear uno heredando de FlutterSceneDelegate:
Abre tu aplicación en Xcode.
-
Haz clic derecho en la carpeta Runner y luego selecciona New Empty File.

-
Crea tu clase
SceneDelegate.Para proyectos de Swift, crea un archivo
SceneDelegate.swift:my_app/ios/Runner/SceneDelegate.swiftswiftimport Flutter import UIKit class SceneDelegate: FlutterSceneDelegate {}Para proyectos de Objective-C, crea un archivo
SceneDelegate.hy un archivoSceneDelegate.m:my_app/ios/Runner/SceneDelegate.hobjc#import <Flutter/Flutter.h> #import <UIKit/UIKit.h> @interface SceneDelegate : FlutterSceneDelegate @endmy_app/ios/Runner/SceneDelegate.mobjc#import "SceneDelegate.h" @implementation SceneDelegate @end -
En tu archivo
Info.plist, cambia el valor de Delegate Class Name (UISceneDelegateClassName) deFlutterSceneDelegatea tu nueva clase.Para proyectos de Swift, usa
$(PRODUCT_MODULE_NAME).SceneDelegate:Info.plistxml<key>UIApplicationSceneManifest</key> <dict> <!-- ... --> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict> <!-- ... --> <key>UISceneDelegateClassName</key> <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string> <!-- ... --> </dict> </array> </dict> </dict>Para proyectos de Objective-C, usa
SceneDelegate:Info.plistxml<key>UIApplicationSceneManifest</key> <dict> <!-- ... --> <key>UISceneConfigurations</key> <dict> <key>UIWindowSceneSessionRoleApplication</key> <array> <dict> <!-- ... --> <key>UISceneDelegateClassName</key> <string>SceneDelegate</string> <!-- ... --> </dict> </array> </dict> </dict>
Migrar una integración add-to-app
#De manera similar a FlutterAppDelegate,
se recomienda usar FlutterSceneDelegate pero no es requerido.
El FlutterSceneDelegate reenvía callbacks de la escena,
como openURL, a plugins como
local_auth.
Crear o actualizar un SceneDelegate
#import UIKit
import Flutter
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
class SceneDelegate: FlutterSceneDelegate {
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@interface SceneDelegate : FlutterSceneDelegate
Al usar Flutter en una aplicación SwiftUI,
opcionalmente puedes usar un FlutterAppDelegate
para recibir eventos de la aplicación.
Para migrarlo y recibir eventos de UIScene,
realiza los siguientes cambios:
-
Establece el scene delegate como
FlutterSceneDelegateenapplication:configurationForConnecting:options::swift@Observable class AppDelegate: FlutterAppDelegate { ... override func application( _ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions ) -> UISceneConfiguration { let configuration = UISceneConfiguration( name: nil, sessionRole: connectingSceneSession.role ) configuration.delegateClass = FlutterSceneDelegate.self return configuration } } -
Si tu aplicación no admite múltiples escenas, establece Enable Multiple Scenes en NO en Application Scene Manifest dentro de las propiedades Info de tu target. El soporte para múltiples escenas está habilitado de forma predeterminada para las aplicaciones SwiftUI.

Si tu aplicación admite múltiples escenas, consulta Si tu aplicación soporta múltiples escenas para obtener más instrucciones.
Si no puedes heredar de FlutterSceneDelegate
#Si no puedes heredar de FlutterSceneDelegate,
usa el protocolo FlutterSceneLifeCycleProvider y un
objeto FlutterPluginSceneLifeCycleDelegate para
reenviar eventos del ciclo de vida de la escena a Flutter.
import Flutter
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate, FlutterSceneLifeCycleProvider
{
var sceneLifeCycleDelegate: FlutterPluginSceneLifeCycleDelegate =
FlutterPluginSceneLifeCycleDelegate()
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
sceneLifeCycleDelegate.scene(
scene,
willConnectTo: session,
options: connectionOptions
)
}
func sceneDidDisconnect(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneDidDisconnect(scene)
}
func sceneWillEnterForeground(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneWillEnterForeground(scene)
}
func sceneDidBecomeActive(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneDidBecomeActive(scene)
}
func sceneWillResignActive(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneWillResignActive(scene)
}
func sceneDidEnterBackground(_ scene: UIScene) {
sceneLifeCycleDelegate.sceneDidEnterBackground(scene)
}
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
sceneLifeCycleDelegate.scene(scene, openURLContexts: URLContexts)
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
sceneLifeCycleDelegate.scene(scene, continue: userActivity)
}
func windowScene(
_ windowScene: UIWindowScene,
performActionFor shortcutItem: UIApplicationShortcutItem,
completionHandler: @escaping (Bool) -> Void
) {
sceneLifeCycleDelegate.windowScene(
windowScene,
performActionFor: shortcutItem,
completionHandler: completionHandler
)
}
}
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate, FlutterSceneLifeCycleProvider>
@property(strong, nonatomic) UIWindow* window;
@property(nonatomic, strong) FlutterPluginSceneLifeCycleDelegate* sceneLifeCycleDelegate;
@end
@implementation SceneDelegate
- (instancetype)init {
if (self = [super init]) {
_sceneLifeCycleDelegate = [[FlutterPluginSceneLifeCycleDelegate alloc] init];
}
return self;
}
- (void)scene:(UIScene*)scene
willConnectToSession:(UISceneSession*)session
options:(UISceneConnectionOptions*)connectionOptions {
[self.sceneLifeCycleDelegate scene:scene willConnectToSession:session options:connectionOptions];
}
- (void)sceneDidDisconnect:(UIScene*)scene {
[self.sceneLifeCycleDelegate sceneDidDisconnect:scene];
}
- (void)sceneDidBecomeActive:(UIScene*)scene {
[self.sceneLifeCycleDelegate sceneDidBecomeActive:scene];
}
- (void)sceneWillResignActive:(UIScene*)scene {
[self.sceneLifeCycleDelegate sceneWillResignActive:scene];
}
- (void)sceneWillEnterForeground:(UIScene*)scene {
[self.sceneLifeCycleDelegate sceneWillEnterForeground:scene];
}
- (void)sceneDidEnterBackground:(UIScene*)scene {
[self.sceneLifeCycleDelegate sceneDidEnterBackground:scene];
}
- (void)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts {
[self.sceneLifeCycleDelegate scene:scene openURLContexts:URLContexts];
}
- (void)scene:(UIScene*)scene continueUserActivity:(NSUserActivity*)userActivity {
[self.sceneLifeCycleDelegate scene:scene continueUserActivity:userActivity];
}
- (void)windowScene:(UIWindowScene*)windowScene
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
completionHandler:(void (^)(BOOL))completionHandler {
[self.sceneLifeCycleDelegate windowScene:windowScene
performActionForShortcutItem:shortcutItem completionHandler:completionHandler];
}
Si tu aplicación soporta múltiples escenas
#Cuando se habilitan múltiples escenas (UIApplicationSupportsMultipleScenes),
Flutter no puede conectar automáticamente un FlutterEngine a su
correspondiente UIScene durante la fase inicial de conexión de la escena.
Para garantizar que los plugins de Flutter puedan recibir las opciones iniciales de configuración
de la escena (como URLs de enlaces profundos o elementos de acceso directo pasados dentro de la
carga útil de UIScene.ConnectionOptions), debes registrar manualmente el
FlutterEngine con tu FlutterSceneDelegate o con tu
FlutterPluginSceneLifeCycleDelegate dentro del método
scene:willConnectToSession:options:.
Si no realizas este registro manual,
el FlutterEngine se registrará automáticamente una vez que la vista
creada por el FlutterViewController se agregue a la jerarquía de vistas activas.
Sin embargo, para ese punto, el motor y sus plugins ya habrán perdido
cualquier evento de conexión de inicio pasado durante willConnectToSession:.
import Flutter
import FlutterPluginRegistrant
import UIKit
class SceneDelegate: FlutterSceneDelegate {
let flutterEngine = FlutterEngine(name: "my flutter engine")
override func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: windowScene)
flutterEngine.run()
GeneratedPluginRegistrant.register(with: flutterEngine)
// If using FlutterSceneDelegate:
self.registerSceneLifeCycle(with: flutterEngine)
// If using FlutterSceneLifeCycleProvider:
// sceneLifeCycleDelegate.registerSceneLifeCycle(with: flutterEngine)
let viewController = ViewController(engine: flutterEngine)
window?.rootViewController = viewController
window?.makeKeyAndVisible()
super.scene(scene, willConnectTo: session, options: connectionOptions)
}
}
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h>
@interface SceneDelegate : FlutterSceneDelegate
@property(nonatomic, strong) FlutterEngine *flutterEngine;
@end
#import "SceneDelegate.h"
#import "ViewController.h"
@implementation SceneDelegate
- (instancetype)init {
if (self = [super init]) {
_flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];
}
return self;
}
- (void)scene:(UIScene *)scene
willConnectToSession:(UISceneSession *)session
options:(UISceneConnectionOptions *)connectionOptions {
if (![scene isKindOfClass:[UIWindowScene class]]) {
return;
}
UIWindowScene *windowScene = (UIWindowScene *)scene;
self.window = [[UIWindow alloc] initWithWindowScene:windowScene];
[self.flutterEngine run];
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];
// If using FlutterSceneDelegate:
[self registerSceneLifeCycleWithFlutterEngine:self.flutterEngine];
// If using FlutterSceneLifeCycleProvider:
// [self.sceneLifeCycleDelegate registerSceneLifeCycleWithFlutterEngine:self.flutterEngine];
ViewController *viewController = [[ViewController alloc] initWithEngine:self.flutterEngine];
self.window.rootViewController = viewController;
[self.window makeKeyAndVisible];
[super scene:scene willConnectToSession:session options:connectionOptions];
}
@end
Si registras manualmente un FlutterEngine con una escena,
también debes anular su registro si la vista
creada por el FlutterEngine cambia de escena.
// If using FlutterSceneDelegate:
self.unregisterSceneLifeCycle(with: flutterEngine)
// If using FlutterSceneLifeCycleProvider:
sceneLifeCycleDelegate.unregisterSceneLifeCycle(with: flutterEngine)
// If using FlutterSceneDelegate:
[self unregisterSceneLifeCycleWithFlutterEngine:self.flutterEngine];
// If using FlutterSceneLifeCycleProvider:
[self.sceneLifeCycleDelegate unregisterSceneLifeCycleWithFlutterEngine:self.flutterEngine];
Migrar un plugin de Flutter
#No todos los plugins usan eventos de ciclo de vida. Sin embargo, si tu plugin lo hace, mígralo al ciclo de vida basado en escenas de UIKit de la siguiente manera:
-
Actualiza las versiones del SDK de Dart y Flutter en tu
pubspec.yaml.Las APIs requeridas para esta migración están disponibles a partir de Flutter 3.38:
pubspec.yamlyamlenvironment: sdk: ^3.10.0 flutter: ">=3.38.0" -
Adopta el protocolo
FlutterSceneLifeCycleDelegate.swiftpublic final class MyPlugin: NSObject, FlutterPlugin { public final class MyPlugin: NSObject, FlutterPlugin, FlutterSceneLifeCycleDelegate {objc@interface MyPlugin : NSObject<FlutterPlugin> @interface MyPlugin : NSObject<FlutterPlugin, FlutterSceneLifeCycleDelegate> -
Registra el plugin como un receptor de llamadas de
UISceneDelegate.Para seguir soportando aplicaciones que aún no se han migrado al ciclo de vida
UIScene, considera seguir registrado en el application delegate y mantener también los eventos deAppDelegate.swiftpublic static func register(with registrar: FlutterPluginRegistrar) { ... registrar.addApplicationDelegate(instance) registrar.addSceneDelegate(instance) }objc+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { ... [registrar addApplicationDelegate:instance]; [registrar addSceneDelegate:instance]; } -
Añade los eventos de escena que tu plugin necesite.
La mayoría de los eventos de UI de
AppDelegatetienen un reemplazo uno a uno, como se muestra en la siguiente tabla. Para obtener detalles sobre cada evento, visita la documentación de Apple paraUISceneDelegateyUIWindowSceneDelegate.Una vez que identifiques los eventos de escena que reemplazan a los eventos de aplicación de los que dependía tu plugin, implementa los métodos correspondientes de
FlutterSceneLifeCycleDelegate. Los siguientes fragmentos muestran la firma de cada evento de escena que admiteFlutterSceneLifeCycleDelegate; implementa solo los que tu plugin necesite.swiftpublic func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions? ) -> Bool { } public func sceneDidDisconnect(_ scene: UIScene) { } public func sceneWillEnterForeground(_ scene: UIScene) { } public func sceneDidBecomeActive(_ scene: UIScene) { } public func sceneWillResignActive(_ scene: UIScene) { } public func sceneDidEnterBackground(_ scene: UIScene) { } public func scene( _ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext> ) -> Bool { } public func scene( _ scene: UIScene, continue userActivity: NSUserActivity ) -> Bool { } public func windowScene( _ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void ) -> Bool { }objc- (BOOL)scene:(UIScene*)scene willConnectToSession:(UISceneSession*)session options:(nullable UISceneConnectionOptions*)connectionOptions { } - (void)sceneDidDisconnect:(UIScene*)scene { } - (void)sceneWillEnterForeground:(UIScene*)scene { } - (void)sceneDidBecomeActive:(UIScene*)scene { } - (void)sceneWillResignActive:(UIScene*)scene { } - (void)sceneDidEnterBackground:(UIScene*)scene { } - (BOOL)scene:(UIScene*)scene openURLContexts:(NSSet<UIOpenURLContext*>*)URLContexts { } - (BOOL)scene:(UIScene*)scene continueUserActivity:(NSUserActivity*)userActivity { } - (BOOL)windowScene:(UIWindowScene*)windowScene performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler { } -
Mueve la lógica de inicio de
application:willFinishLaunchingWithOptions:yapplication:didFinishLaunchingWithOptions:ascene:willConnectToSession:options:.Aunque
application:willFinishLaunchingWithOptions:yapplication:didFinishLaunchingWithOptions:no están deprecados, sus opciones de inicio sonnildespués de migrar al ciclo de vidaUIScene. Mueve cualquier lógica que dependa de las opciones de inicio al eventoscene:willConnectToSession:options:. -
Opcional: Migra otras APIs deprecadas para soportar múltiples escenas en el futuro.
En lugar de acceder a estas APIs, accede a la
windowScenea través delviewController, como se muestra en los siguientes ejemplos.swiftpublic 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 } }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.registrar.viewController.view.window.windowScene.windows; }
Uso personalizado de FlutterViewController
#Si tu aplicación instancía un FlutterViewController a partir de
storyboards en application:didFinishLaunchingWithOptions: por
razones distintas a la creación de canales de plataforma,
debes adaptarte al nuevo orden de inicialización.
Para hacerlo, utiliza una de las siguientes opciones de migración:
- Crea una subclase de
FlutterViewControllery coloca la lógica en el métodoawakeFromNibde la subclase. - Especifica un
UISceneDelegateen elInfo.plisto en elUIApplicationDelegate, y coloca la lógica enscene:willConnectToSession:options:. Para obtener más información, consulta la documentación de Apple.
Ejemplo
#@objc class MyViewController: FlutterViewController {
override func awakeFromNib() {
super.awakeFromNib()
doSomethingWithFlutterViewController(self)
}
}
Ocultar advertencia de migración
#Para ocultar la advertencia de la CLI de Flutter sobre la migración a UIScene,
añade lo siguiente a tu pubspec.yaml:
flutter:
config:
enable-uiscene-migration: false
Deshabilitar temporalmente UIScene
#Para deshabilitar temporalmente el soporte de UIScene, añade un guion bajo (_)
delante de Application Scene Manifest en tu Info.plist:
Cuando estés listo para volver a habilitar el soporte de UIScene, elimina el guion bajo.
Timeline
#Llegó en la versión: 3.38.0-0.1.pre
En la versión estable: 3.38
Apple aún no ha anunciado cuándo impondrá el requisito de UIScene.
Una vez que Apple cambie su advertencia por una aserción,
las aplicaciones de Flutter que no hayan adoptado el ciclo de vida UIScene
fallarán al iniciarse cuando se compilen con el último SDK.
Referencias
#- Incidencia 167267: El problema inicial reportado.
A menos que se indique lo contrario, la documentación en este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-06-10. Ver código fuente oreportar un problema.