The iOS 27 SDK makes the UIScene lifecycle mandatory for every UIKit-based app — Flutter included. A Flutter app compiled with Xcode 27 that skips this transition crashes on launch. This post walks through what Flutter migrates automatically versus what needs manual work, and how to scan your plugins for dependencies on the old lifecycle.
💡 Pro Tip: Before you start the migration, runflutter --versionand confirm you're on Flutter 3.41 or later — UIScene support has been on by default since that release, and the CLI automatically migrates anyAppDelegateyou haven't customized.
Table of Contents
- The iOS 27 UIScene Mandate in One Sentence
- What the Flutter CLI Handles Automatically vs. What Needs Manual Migration
- The Info.plist + AppDelegate Changes, Step by Step
- Finding Custom Native Code and Plugins That Depend on the Legacy Lifecycle
- The Cost of the Minimum iOS 15 / macOS 12 Decision
- The End of Intel Mac Support
- Deeplink/Push/State-Restore Regression Testing
- The Same Apple Constraint on the Capacitor and React Native Side
- Don't Confuse This With Unrelated Changes in the Same Release
- Verification Checklist
- FAQ
- How do I migrate a Flutter app to the UIScene lifecycle?
- What's the minimum iOS version for Flutter 3.47?
- Will my app fail to launch if I don't migrate to UIScene?
- How do you migrate from AppDelegate to SceneDelegate in Flutter?
- Does this immediately affect my app that's already live on the App Store?
- Does this only affect Flutter, or every cross-platform framework?
- Conclusion
- Sources
The iOS 27 UIScene Mandate in One Sentence
Flutter's official 3.47 announcement describes this change in plain terms: "The iOS 27 SDK now mandates the UIScene lifecycle for all UIKit-based apps. Apps built with Xcode 27 that do not adopt UIScene will fail to launch on startup." So this isn't a warning — it's a direct launch failure. The rule isn't specific to Flutter either; Apple announced it for the whole UIKit ecosystem at WWDC25. In Apple's own words: starting with the release that follows iOS 26, any UIKit app built with the latest SDK must use the UIScene lifecycle or it will not launch. On iOS 26 this only printed a console warning; once built with the iOS 27 SDK it means a crash.
Don't conflate this with a separate rule: the "App Store SDK build requirement," effective April 28, 2026, required building against the iOS 26 SDK. The UIScene mandate is a different threshold — it applies to any app built with Xcode 27.
Note: at the time this post was published (early September 2026), the iOS 27 SDK is still in beta; the Xcode 27 beta was opened to developers on June 8, 2026 (first build: 27A5194q).
What the Flutter CLI Handles Automatically vs. What Needs Manual Migration
The good news: UIScene support has been on by default since Flutter 3.41. The official migration guide states: "As of Flutter 3.41, UIScene is supported by default. If your AppDelegate hasn't been customized, the Flutter CLI automatically migrates your app." So if you've never touched AppDelegate.swift, running flutter build ios or flutter run has the CLI perform the migration for you in the background, and on success it prints a clear message: "Finished migration to UIScene lifecycle" — if you see that message, no further action is needed.
Flutter's 3.47 announcement draws a clear line here: "For most apps, the Flutter CLI handles this migration automatically during the build. However, manual migration is required if you have custom native code in your AppDelegate or use plugins that still rely on the legacy application lifecycle." So manual migration is triggered by one of two situations:
- A customized
AppDelegate: you've added your own native code insideapplication:didFinishLaunchingWithOptions:— push token registration, third-party SDK initialization, deep link routing, and so on. - A plugin dependent on the legacy lifecycle: the plugin listens for now-deprecated UI-state events like
applicationDidBecomeActive.
There's also a scope distinction: the official guide points to separate migration guides for Flutter setups embedded in an existing native iOS app (add-to-app) and for Flutter plugins that use iOS application lifecycle events. If you maintain a plugin, that guide — not the steps below — is your path.
The fastest way to find out whether you need manual migration is to read the flutter build ios output. If the CLI can't complete the automatic migration, it doesn't stay silent — it prints a warning and lists what you need to do by hand:
1flutter build ios --release2# What to look for in the output:3# "Finished migration to UIScene lifecycle" -> automatic migration succeeded, nothing else to do4# if this message is missing, the CLI prints a warning with manual migration instructions -> proceed to the steps belowThe Info.plist + AppDelegate Changes, Step by Step
Manual migration touches two files: Info.plist and AppDelegate.swift. First, you add a UIApplicationSceneManifest entry to Info.plist; the default scene delegate class is FlutterSceneDelegate:
1<key>UIApplicationSceneManifest</key>2<dict>3 <key>UIApplicationSupportsMultipleScenes</key>4 <false/>5 <key>UISceneConfigurations</key>6 <dict>7 <key>UIWindowSceneSessionRoleApplication</key>8 <array>9 <dict>10 <key>UISceneClassName</key>11 <string>UIWindowScene</string>12 <key>UISceneDelegateClassName</key>13 <string>FlutterSceneDelegate</string>14 <key>UISceneConfigurationName</key>15 <string>flutter</string>16 <key>UISceneStoryboardFile</key>17 <string>Main</string>18 </dict>19 </array>20 </dict>21</dict>Next, plugin registration inside AppDelegate.swift moves from application:didFinishLaunchingWithOptions: to the new didInitializeImplicitFlutterEngine callback. For this, AppDelegate needs to conform to the FlutterImplicitEngineDelegate protocol:
1import Flutter2import UIKit3 4@main5@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {6 func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {7 GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)8 }9}If your project needs custom scene behavior (say, multi-window support or custom deep-link handling), you can write your own SceneDelegate — but the official guide is explicit here: "If you implement your own SceneDelegate, you must subclass FlutterSceneDelegate or conform to the FlutterSceneLifeCycleProvider protocol." If you write a UISceneDelegate from scratch and follow neither path, scene lifecycle events never reach Flutter.
Finding Custom Native Code and Plugins That Depend on the Legacy Lifecycle
The sneakiest source of bugs is relying on an AppDelegate lifecycle method that no longer gets called. Apple has deprecated the UI-state-related application lifecycle events; the official documentation summarizes it this way: "Apple has deprecated application lifecycle events related to UI state. After you migrate to the UIScene lifecycle, UIKit no longer calls these events." So code you've written inside methods like applicationDidBecomeActive or applicationWillResignActive may simply stop firing — a silent, hard-to-notice regression.
Here's how the checklist plays out:
grepAppDelegate.swiftfor everyUIApplicationDelegatemethod other thanapplication(_:did...).- For every native plugin in
Podfile.lock, check its GitHub repo for an open issue mentioning "UIScene" or "scene lifecycle". - Check the latest release notes of any third-party SDK (analytics, crash reporting) doing push notifications, background fetch, or URL-scheme handling.
One concrete example: the widely used flutter_local_notifications package completed this migration on February 22, 2026. Tracking issue #2751 was closed by PR #2761, which moved the example app to UIScene and updated the README for apps already migrated. So this particular package is no longer a risk item — but run the same check yourself for every package you depend on.
Rather than opening files one by one, it's safer to scan for deprecated lifecycle methods with a single command. The grep below lists UI-state events inside AppDelegate.swift that may no longer be called:
1grep -nE "applicationDidBecomeActive|applicationWillResignActive|applicationDidEnterBackground|applicationWillEnterForeground" \2 ios/Runner/AppDelegate.swiftIf this returns a result, consider moving that logic to its scene-based equivalent (sceneDidBecomeActive, sceneWillResignActive, etc.) — UIKit no longer fires these events, so business-critical logic in there (session refresh, an analytics event) silently stopping is a regression just as serious as a crash, even if far less visible.
Listing your native dependencies from Podfile.lock and searching GitHub for each one also makes this easier:
1grep -E "^[[:space:]]+- " ios/Podfile.lock | sort -uFrom that list, check each package's latest release notes and open issues to spot ahead of time which dependency isn't UIScene-ready.
The table below summarizes the most commonly confused responsibility shift during migration — which code now belongs where:
Old location (application lifecycle) | New location (UIScene lifecycle) | Note |
|---|---|---|
Plugin registration inside application:didFinishLaunchingWithOptions: | didInitializeImplicitFlutterEngine(_:) | Requires the FlutterImplicitEngineDelegate protocol |
applicationDidBecomeActive | No longer called by UIKit | UI-state events are deprecated |
Custom SceneDelegate logic | FlutterSceneDelegate subclass, or FlutterSceneLifeCycleProvider | If written from scratch, scene events aren't delivered |
No scene definition in Info.plist | UIApplicationSceneManifest entry | Default UISceneDelegateClassName is FlutterSceneDelegate |
The Cost of the Minimum iOS 15 / macOS 12 Decision
Along with Xcode 27 support, Flutter also raised the minimum supported OS versions: on iOS, the previous minimum of 13 went up to 15; on macOS, 10.15 went up to 12 (Flutter 3.47 announcement). This change was decided and closed under issue #187741 in the flutter/flutter repository, titled "Increase iOS minimum supported version from 13 to 15 to support Xcode 27."
There's no official figure for how many devices this affects on the user side; neither Apple nor the Flutter team has shared the real percentage of users this raise would drop. Pull the iOS version distribution from your own app's analytics dashboard (Firebase Analytics, App Store Connect, or your own telemetry) and see what share of your users is still below iOS 15. If that share is negligible, the raise is a non-issue; if it's high, consider announcing the minimum SDK bump in your App Store release notes ahead of time.
The End of Intel Mac Support
In that same 3.47 release, Flutter also began narrowing support for Intel-based Macs. The official announcement is explicit: "We have disabled automated test runs on Intel hardware, and the Flutter CLI now prints warnings when building on Intel hosts or targeting dual architectures. These warnings will become errors in a future release." So today it's only a warning, but in a future release it will turn into a build error.
If you're building a macOS-targeted Flutter app and you're ready to move to Apple Silicon, you can switch to an ARM64-only build right away:
1flutter config --enable-macos-arm64-only2flutter build macos --releaseThis command stops producing a universal binary and sets up a build chain that targets Apple Silicon only — make sure your CI servers also use Apple Silicon runners; on an Intel build machine, the CLI currently only prints a warning, but Flutter has announced that this warning will become an error in a future release.
Deeplink/Push/State-Restore Regression Testing
The most damaging UIScene migration bugs don't show up at build time — they show up at runtime. The official Flutter documentation points to two concrete risks: custom SceneDelegate implementations that don't forward scene lifecycle events to Flutter, and deprecated UI-state events that no longer fire. In practice, these two technical facts mean five scenarios need regression testing — this isn't an official checklist, it's the logical consequence of those two risks:
- Cold start + deep link: with the app closed, tap a deep link and confirm it routes to the correct screen.
- Launch from a home-screen shortcut: check whether launching via a 3D Touch/Home Screen quick action is affected by plugin registration.
- Background/foreground transition: verify that code depending on events like
applicationDidBecomeActiveno longer runs, and that its scene-based counterpart (sceneDidBecomeActive) is active instead. - Push notification handling: test that launching from a tapped notification works correctly with the plugin's new
didInitializeImplicitFlutterEnginecallback. - State restoration: after the system terminates the app in the background, confirm that on relaunch the scene state and the screen the user was on are restored.
The Same Apple Constraint on the Capacitor and React Native Side
This mandate isn't specific to Flutter — the same Apple rule hits every cross-platform ecosystem. Capacitor met it head-on with a version bump: "we've released Capacitor 8.5, a breaking minor that adopts UIScene... Changes in this version are iOS only, and for most apps the migration is one CLI command." The migration command is a single line:
1npm i -D @capacitor/cli@latest && npx cap migrateThere's an important distinction here: this change only affects apps _rebuilt_ with Xcode 27. In the Capacitor team's own words: "this only affects apps built with Xcode 27. Published apps keep running on iOS 27... Nothing breaks in the App Store when iOS 27 ships." So an app already live on the App Store, built with an older Xcode, keeps working once iOS 27 ships — the problem only surfaces when you want to ship a _new_ build. The same logic applies to Flutter: your existing binary is unaffected, but you need to have completed the migration by the time you build your next App Store update with Xcode 27.
On the React Native side, the story is mostly concrete bug reports piling up on GitHub. One open React Native issue is titled "[iOS] CLIENT OF UIKIT REQUIRES UPDATE: This process does not adopt UIScene lifecycle. This will become an assert in a future version." Expo has a similar report: "[iOS][Xcode 27][SDK 56] Prebuild template fails to launch because UIScene lifecycle is required." In short: the React Native ecosystem faces the same Apple constraint, tracked for now mostly through GitHub issues.
Don't Confuse This With Unrelated Changes in the Same Release
Flutter's 3.47 announcement bundles more than one topic into the same post — which creates a real risk of confusing the UIScene migration with an unrelated decision. The same release notes also cover a design-widget migration you run with dart fix --apply --code=migrate_design_widgets, and modularization into standalone packages like material_ui/cupertino_ui. These two are on a completely different axis from UIScene: UIScene is an Apple/iOS SDK requirement, while splitting out the design packages is Flutter's own decision to break its framework architecture into smaller pieces. Doing one doesn't mean you need to do the other — as you get your project ready for Xcode 27, you don't need to change your design-package imports in pubspec.yaml; that's a separate migration step.
There's a similar risk with dates: the April 28, 2026 App Store requirement to build against the iOS 26 SDK is a different threshold entirely. That rule checked which SDK the app was built with at submission time; the UIScene mandate kicks in at runtime, on launch. Treating both as "handled in April 2026" risks a surprise crash the moment you rebuild with Xcode 27.
If your project targets more than one platform (iOS + iPadOS + macOS Catalyst), don't forget to check the deployment target separately for each — flutter build ios, flutter build ipa, and flutter build macos each read their own Info.plist/project.pbxproj settings, and fixing one while skipping the other is a common mistake.
Verification Checklist
Once you've completed the migration, check off the items below in order. This list summarizes the official-source requirements covered in the sections above:
Check | How to verify it |
|---|---|
flutter build ios output | Did you see the "Finished migration to UIScene lifecycle" message? |
Info.plist | Is there a UIApplicationSceneManifest entry, and is UISceneDelegateClassName present? |
AppDelegate.swift | Does it conform to FlutterImplicitEngineDelegate, and is plugin registration inside didInitializeImplicitFlutterEngine? |
Legacy lifecycle methods | Is there any remaining code depending on UI-state events like applicationDidBecomeActive? |
Native plugins | Does the README/issue tracker of each package you use mention UIScene? |
Minimum OS | Is IPHONEOS_DEPLOYMENT_TARGET in project.pbxproj set to 15.0, and MACOSX_DEPLOYMENT_TARGET to 12.0 if you have a macOS target? |
Deep link / push / state restore | Have all five scenarios been manually tested on a real device? |
GOLDEN TIP
The most valuable insight in this article
This tip holds the article's most important takeaway.
Easter Egg
You found a hidden gem!
There's a hidden detail in this section. Want to uncover it?
Reader Reward
Below is a one-page summary of every step in this post, something you can quickly review on your own project before migrating. Print it, keep it handy on migration day.
FAQ
How do I migrate a Flutter app to the UIScene lifecycle?
Since Flutter 3.41, UIScene support is on by default: flutter run/flutter build ios auto-migrates an uncustomized AppDelegate and prints "Finished migration to UIScene lifecycle." With custom native code or a plugin depending on legacy lifecycle APIs, migrate manually: conform AppDelegate to FlutterImplicitEngineDelegate and move plugin registration to didInitializeImplicitFlutterEngine.
What's the minimum iOS version for Flutter 3.47?
Flutter 3.47 raised the minimum iOS version from 13 to 15, and the minimum macOS version from 10.15 to 12, to support Xcode 27. This decision was settled and closed under issue #187741 in the flutter/flutter GitHub repository.
Will my app fail to launch if I don't migrate to UIScene?
Yes — UIKit-based apps built with the iOS 27 SDK that don't adopt the UIScene lifecycle crash on launch. This is a requirement Apple announced at WWDC25, becoming mandatory in the release following iOS 26; on iOS 26 it only produced a console warning, but once built with the iOS 27 SDK it's a crash.
How do you migrate from AppDelegate to SceneDelegate in Flutter?
UISceneDelegate takes over the UI lifecycle; AppDelegate keeps only process events and the general app lifecycle. Plugin registration moves from application:didFinishLaunchingWithOptions: to didInitializeImplicitFlutterEngine; you also add a UIApplicationSceneManifest entry to Info.plist.
Does this immediately affect my app that's already live on the App Store?
No. As the Capacitor team also confirmed, the rule only affects apps _rebuilt_ with Xcode 27 — a binary already live, built with an older Xcode, keeps working on iOS 27. The problem only arises when you build your next update with Xcode 27.
Does this only affect Flutter, or every cross-platform framework?
Every UIKit-based app is affected. Capacitor addressed this migration officially with version 8.5; on the React Native and Expo side, the issue is being tracked mostly through GitHub issue trackers, with open reports of the same failure.
Conclusion
For most Flutter projects, the UIScene migration is a background operation that completes silently with a single flutter build ios command. All the risk concentrates in projects that have customized AppDelegate or use a plugin dependent on legacy lifecycle events — in that case, there are a few manual changes you need to make in Info.plist and AppDelegate, and don't call the migration "done" without testing the deep link/push/state-restore scenarios on a real device.
For native bridges, see Flutter and iOS Integration: Platform Channels and Native Modules. On the performance side, see Flutter 4 Impeller: New Render Engine and Performance Revolution 2026. For structuring a project at scale, see Flutter Clean Architecture: A Layered Architecture Guide. Weighing a cross-platform choice? See React Native vs Flutter 2026: A Comprehensive Comparison Guide and Flutter vs SwiftUI: A 3-Year + 60K LOC Production Comparison. For parallel iOS-side platform changes, see iOS 26 Liquid Glass: A SwiftUI Adaptation Guide.
Sources
- Flutter 3.47 Announcement — What's new in Flutter 3.47 — the official announcement of the UIScene mandate, the minimum OS raise, and the narrowing of Intel Mac support.
- Flutter Breaking Changes — UIScene Adoption — AppDelegate/SceneDelegate code samples, Info.plist configuration, and the Apple WWDC25 quote.
- Capacitor 8.5 Released — Capacitor's official response to the same Apple constraint and its single-command migration instructions.
- flutter_local_notifications — GitHub PR #2761 — the UIScene migration a widely used notification package completed in February 2026; the record that closed issue #2751.
- Apple Developer — Xcode 27 beta (27A5194q) — the official June 8, 2026 release record for the Xcode 27 beta.
- Apple Developer — Upcoming SDK Minimum Requirements — the App Store's iOS 26 SDK build threshold, effective April 28, 2026.
- flutter/flutter — Issue #187741 — the closed record of the decision to raise the minimum iOS version from 13 to 15.
- react-native — GitHub Issue #54739 — the concrete failure output of the UIScene mandate on the React Native side.
- expo — GitHub Issue #46664 — the same launch failure Expo's prebuild template hits with Xcode 27.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

