Most Flutter apps adapt well to iPhone Duo, because Flutter’s layout system is resolution-independent. A handful of habits break, and one open question — whether displayFeatures is populated on iOS — decides how fold-aware you can be. The checklist below covers all of it, and each item links to the detail.
Flutter iPhone Duo checklist
- Build with Xcode 27.1.
flutter build iosuses the selected Xcode, and only the iOS 27.1 SDK reaches the screen edge. - Read size in
build. UseMediaQuery.sizeOf(context); a size cached inStategoes stale after the first fold. - Drop orientation as a layout strategy. The inner display ignores
setPreferredOrientations, so branch on width instead. - Pad left and right separately.
MediaQuery.paddingOf(context)exposes both;EdgeInsets.symmetricassumes they match. - Branch on width, not platform. A 600-point breakpoint fires on the 626-point inner display, where your tablet layout belongs.
- Keep media playing while visible. Pause on
AppLifecycleState.paused, notinactive— an app in Split View can be on screen yet inactive. - Rethink full-screen 16:9 video. Put the player in a
Columnwith related content beneath it. - Log
displayFeatureson a real build. Whether iOS populates it is unconfirmed, so assume it is empty until you see otherwise. - Design Flutter-drawn tab bars on purpose. System bars move to the side on iPhone Duo;
CupertinoTabBarandNavigationBardo not. - Test the camera across a fold. Confirm your camera plugin’s preview survives folding and unfolding.
- Test at 466 and 626 points before hardware arrives, with an iPad simulator in Split View and snapshot tests.
- Treat App Store screenshots as optional for now. Apple lists iPhone Duo sizes, but uploads open later this year.
Google has published no iPhone Duo guidance, so the Flutter side of this is our analysis. If you also ship on Android foldables, how the two platforms compare maps displayFeatures onto Apple’s equivalent.
Flutter is in a genuinely unusual position. It is the only major cross-platform framework that already has a correct abstraction for foldables — built years ago for Android devices — and the open question is not what the API should look like but whether it is connected on iOS.
The abstraction Flutter already has
MediaQuery.displayFeatures reports hinges and cutouts with bounds and state. It was designed for Android foldables and dual-screen devices, and the TwoPane widget was built on top of it.
final features = MediaQuery.of(context).displayFeatures;
final hinges = features.where((f) => f.type == DisplayFeatureType.hinge);
Conceptually this maps almost exactly onto Apple’s model. Apple’s division regions are hinges that split an area; Apple’s occlusion regions are cutouts that cover one. Flutter’s DisplayFeatureType already distinguishes those cases. Even the active/inactive distinction has an analogue in DisplayFeatureState.
Someone designing a Flutter API for iPhone Duo from scratch would land close to what already exists.
The open question
Whether the iOS embedder populates displayFeatures from Apple’s reservedRegions API is not something we can determine from Apple’s material, and Flutter has announced nothing.
Assume it is empty until you have verified otherwise on a real build. Log it early:
@override
Widget build(BuildContext context) {
final features = MediaQuery.of(context).displayFeatures;
debugPrint('displayFeatures on this device: $features');
// …
}
If it is populated, the Android foldable patterns transfer directly and Flutter is in the best position of any cross-platform stack.
If it is empty, you have the same options as React Native: a platform channel wrapping reservedRegions(kind:) and pushing frames into Dart, or fold-unaware but correct layout. Given how well the existing abstraction fits, a package filling this gap seems likely to appear — but nothing has been announced, and you should not plan around it.
What works regardless
Flutter’s layout system is resolution-independent and adapts to any size. The core migration is the familiar discipline problem.
Read size in build, never cache it.
// Wrong — captured once, stale after the first fold
late final double _width;
@override
void initState() {
super.initState();
_width = MediaQuery.of(context).size.width;
}
// Right
@override
Widget build(BuildContext context) {
final width = MediaQuery.sizeOf(context).width;
return width > 600 ? const TwoColumn() : const SingleColumn();
}
MediaQuery.sizeOf is preferable to MediaQuery.of(context).size — it only rebuilds on size changes rather than on every MediaQuery change, which matters on a device where these change often.
Use LayoutBuilder for decisions about a subtree’s own constraints rather than the whole window. On iPhone Duo the difference is real, because Split View means the window is frequently smaller than the display.
Treat horizontal padding as two values. Flutter has always exposed them separately, so this is easier here than in most stacks:
final padding = MediaQuery.paddingOf(context);
// padding.left and padding.right genuinely differ on iPhone Duo
Any code doing padding.horizontal / 2, or applying EdgeInsets.symmetric(horizontal: padding.left), is asserting a symmetry that no longer holds.
Drop orientation as a layout strategy.
// This does not work on the inner display
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
Apple states the inner display does not honor supported interface orientations. OrientationBuilder is similarly unhelpful — it tells you the aspect ratio, which on a 1.42 display is not the signal you want. Branch on width.
The breakpoint you already have is probably right
The inner display is 626 × 890 points, and Apple describes it as regular-width with room for a sidebar. Most Flutter apps already have a tablet breakpoint somewhere around 600 logical pixels. That breakpoint will now fire on an iPhone, and the layout behind it is very likely the correct one.
Verify it is driven by size and not by platform:
// Wrong — this is an iPhone
final isTablet = defaultTargetPlatform == TargetPlatform.iOS && !isPhone;
// Right
final isWide = MediaQuery.sizeOf(context).width >= 600;
Aspect ratio and media
The inner display’s 1.42 ratio is much squarer than the roughly 2.17 of a current iPhone. A Container with aspectRatio: 16/9 letterboxes heavily — fitted to width, a 16:9 video leaves around a fifth of the display as bars.
Apple’s own guidance is to use that space rather than fight it. In Flutter terms, a Column with the player at a fixed aspect and a list beneath it is the natural equivalent of an ArrangementView split — and it is a layout Flutter has always been able to express.
For documents and feeds, 1.42 is close to √2, the A-series paper proportion. Page-shaped Flutter layouts fit this display unusually well.
Split View and lifecycle
All apps participate in side-by-side multitasking. Two things to test:
- Drag the divider through its full range and confirm continuous reflow.
MediaQueryhandles it; cached values do not. - Check
AppLifecycleStatehandling. In Split View your app can be visible but not resumed. Anything paused oninactive— video, animation controllers, timers — will look frozen to a user who can still see it.
Tab bars and the camera
On iPhone Duo, system toolbars and tab bars move to a side column on the closed outer display and on the inner display in landscape. Flutter draws its own widgets, including CupertinoTabBar and Material’s NavigationBar, so the system has nothing to move: your bar stays wherever your layout puts it. That is not a bug, but it is a decision. Design the bar deliberately for the short, wide outer display, and consider a NavigationRail on wide layouts, which is Flutter’s own way of putting navigation at the side.
Camera apps have a related gap. Folding can change which front camera faces the user mid-session, and Apple’s native answer is AVCaptureDeviceDirectionCoordinator. Whether your camera plugin adopts it is up to the plugin, not your Dart code, so start a live preview and fold and unfold the device before you release.
A pragmatic migration
- Log
displayFeatureson a real iPhone Duo build and find out where you stand - Replace cached
MediaQueryreads withsizeOfinbuild - Audit
paddingOfconsumers for symmetric assumptions - Replace platform-based layout checks with width checks
- Remove orientation preferences used as a layout strategy
- Test the Split View divider and the lifecycle-pause path
- Only then consider a platform channel for reserved regions
Steps 2 to 5 are most of the visible improvement, and none of them depend on the answer to step 1.