duo_fold library
Fold-aware layout for Apple's iPhone Duo, extracted from Sensecast.
See README.md for the design notes and the simulator-only caveat.
Classes
- DuoBookSplitNotifier
- DuoFoldBridge
-
Thin wrapper over the foldable plugin so tests can substitute fakes.
The machine-id channel is how the provider seeds a Duo on first launch:
the app answers
hw.machine(e.g.iPhone19,4) over a method channel. The default channel name is the one Sensecast's AppDelegate answers; pass your own name if your native side registers a different channel, or override machineIdentifier outright. - DuoFoldLayoutSignature
- The fields of a DuoFoldState that a layout decision can read.
- DuoFoldState
- Immutable snapshot of Duo fold state. Everything downstream keys off this; on non-Duo devices it is a constant no-op value.
- DuoGlanceEnabledNotifier
- EdgeRailGeometry
-
FoldConfinedRoute<
T> - A pushed route whose content confines itself to the fold's secondary (detail) pane while a vertical split is in effect. Readable content like search results must not straddle the physical crease. The exposed primary pane keeps the previous route visible under a dismissible barrier, mirroring the split-Settings detail idiom.
- FoldLayout
- Pure decisions about fold geometry. Nothing here touches the platform. Every input arrives via DuoFoldState and the caller's constraints.
- FoldSplitView
- FoldStartupGate
Enums
- DuoHingeStatus
- Hinge posture as reported by the foldable bridge.
- DuoPose
- What the device is physically doing, derived from posture + regions.
- FoldMode
- The resolved layout treatment for the current fold state.
Constants
-
foldableMachineIds
→ const Set<
String> -
Machine ids that are certainly foldables, so a first launch can seed
isDuobefore the hinge interaction answers. iPhone Duo reportsiPhone19,4(AppleDB). A stored verdict always wins over this map: a device that was settled non-foldable once is never re-seeded by it.
Properties
-
duoBookSplitProvider
→ NotifierProvider<
DuoBookSplitNotifier, bool> -
final
-
duoFoldBridgeProvider
→ Provider<
DuoFoldBridge> -
final
-
duoFoldProvider
→ StreamProvider<
DuoFoldState> -
Latest fold state. The native
changesstream is open-ended, so consumers must read.value.duoFoldProvider.futurewaits for the stream's last event and would never resolve while the device lives.final -
duoGlanceEnabledProvider
→ NotifierProvider<
DuoGlanceEnabledNotifier, bool> -
final
Functions
-
duoFoldForLayout(
WidgetRef ref) → DuoFoldState -
Watches the fold state at layout granularity: the calling widget
rebuilds when any field a layout decision can read changes (pose,
division/occlusion bands, size classes, hinge status) but not on
raw hinge-angle ticks inside one pose, which a physical sweep emits
continuously (pose carries every angle-derived layout rule). Listeners
that need each event keep
ref.listen(duoFoldProvider, …). -
duoStateFromFoldable(
FoldableData data) → DuoFoldState -
Maps a plugin snapshot into our model. A verdict is settled when the
bridge has stopped answering
unknown: that is the signal the startup gate waits for, and the only verdict the cache persists. FoldableData.unsupported is the settled "not a foldable" answer, not the unsettled one (that const carries supportLevel available). -
foldAwareDialogBuilder(
BuildContext context, WidgetBuilder builder) → WidgetBuilder - Wraps a dialog's builder content so it centers inside the primary pane instead of straddling the fold. Pass-through on non-split modes. The pane anchor is re-evaluated on every fold event, so a dialog already on screen follows the device through book, flat, and table poses.
-
showFoldAwareDialog<
T> ({required BuildContext context, required WidgetBuilder builder, bool barrierDismissible = true}) → Future< T?> -
showDialogthat anchors the dialog inside the active fold pane. Same fold rules as showFoldAwareSheet; outside an active fold it calls showDialog untouched. -
showFoldAwareSheet<
T> ({required BuildContext context, required WidgetBuilder builder, bool isScrollControlled = false, Color? backgroundColor, ShapeBorder? shape, bool showDragHandle = false}) → Future< T?> - Like showModalBottomSheet, but on a folded Duo the sheet is confined to a pane: the primary pane for a book fold (left in LTR, right in RTL), the bottom pane for a table fold. The confinement is re-evaluated while the sheet is open: a mid-presentation fold transition moves the sheet to the matching pane instead of leaving it across the crease.