# Flutter on iPhone Duo: the right abstraction, possibly unwired > Flutter already has a foldable API from Android — MediaQuery.displayFeatures. An analysis of whether it reaches iPhone Duo, and what to do either way. Source: https://iphoneduosupport.com/blog/flutter-iphone-duo/ Published: 2026-09-09 Framework: flutter Status: ENGINEERING ANALYSIS — this framework vendor has published no iPhone Duo guidance. Apple-side facts are sourced; framework-side conclusions are inference. Sources: https://developer.apple.com/videos/play/tech-talks/111461/ https://developer.apple.com/videos/play/tech-talks/111463/ Last verified: 2026-09-09 --- Google has published no iPhone Duo guidance. This is our analysis of how Flutter behaves against the APIs and behaviours Apple documented on 9 September 2026. 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. ```dart 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: ```dart @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.** ```dart // 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: ```dart 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.** ```dart // 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: ```dart // 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. `MediaQuery` handles it; cached values do not. - Check `AppLifecycleState` handling. In Split View your app can be visible but not resumed. Anything paused on `inactive` — video, animation controllers, timers — will look frozen to a user who can still see it. ## A pragmatic migration 1. Log `displayFeatures` on a real iPhone Duo build and find out where you stand 2. Replace cached `MediaQuery` reads with `sizeOf` in `build` 3. Audit `paddingOf` consumers for symmetric assumptions 4. Replace platform-based layout checks with width checks 5. Remove orientation preferences used as a layout strategy 6. Test the Split View divider and the lifecycle-pause path 7. 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. --- Published by iPhone Duo Support (https://iphoneduosupport.com). Not affiliated with Apple Inc.