-
Notifications
You must be signed in to change notification settings - Fork 188
Compose
floatingx-compose lets the window content be a composition: compose { control -> … }.
The composition runs on the window's own FxComposeOwner, so viewModel() and
rememberSaveable state is not cleared when the host Activity is destroyed. The module is
minSdk 23, see Getting Started.
FloatingX.install("compose") {
compose { control ->
val vm: CounterViewModel = viewModel() // the window's own ViewModelStore
var count by rememberSaveable { mutableIntStateOf(0) } // survives container detach
val state by control.stateFlow().collectAsState() // FxState
val pos by control.positionFlow().collectAsState() // screen coords of the content's top-left
Surface(shape = CircleShape, modifier = Modifier.size(110.dp)) {
Column(Modifier.clickable { count++; vm.clicks++ }) {
Text("count $count")
Text("${pos.x.toInt()},${pos.y.toInt()} $state")
}
}
}
anchor(FxGravity.CENTER_START, dy = -100f)
appHost(app)
}.show()compose {} only sets the content; every other option (anchor / adsorption / gestures / animation /
persistence) works exactly as it does for View content, see Configuration.
Every window owns an FxComposeOwner, which is at once a LifecycleOwner, ViewModelStoreOwner and
SavedStateRegistryOwner. It belongs to the control, not to the host Activity, and follows the
window:
| Window action | owner's Lifecycle.State |
|---|---|
attach (container attached to the host) |
STARTED |
show |
RESUMED |
detach (page change, blacklist detach, rotation recreate) |
CREATED |
cancel() |
DESTROYED (terminal) |
Only cancel() reaches DESTROYED, so page changes, rotation and blacklist detach never restart the
composition (#210/#239).
-
viewModel()lands in the owner'sViewModelStore; it lives frominstalltocancel, independent of any page. -
rememberSaveablegoes through the window's own bridging registry: saved when the composition is disposed (container detach) and restored on recomposition.
Does rememberSaveable inside compose {} survive process death?
No. It is saved in-process only: when the composition is disposed (container detach) the state
goes into the window's own bridging registry and is restored on recomposition. Kill the process and
it is gone (the window itself is not recreated either). Persist it yourself if you need more.
public fun FxControl.stateFlow(): StateFlow<FxState>
public fun FxControl.positionFlow(): StateFlow<FxPoint>-
stateFlow()— the window state (INSTALLED/ATTACHED/SHOWN/CANCELLED). -
positionFlow()— the screen coordinates of the content's top-left (the same ascontrol.position; updated every frame while dragging and whenmoveToor adsorption finishes). Note this differs from the x/y in theFxListener.onDrag/onDragEndcallbacks, which are relative to the container. - Both must be called on the main thread, and the flows are only updated there; repeated calls on one control return the same flow.
- After
cancel()core clears its listeners, so the flow stops on its last value.
compose {} does not care where the window is attached — app-level and system windows are written
exactly the same way, only the host lines change:
FloatingX.install("compose-sys") {
compose { control -> /* exactly the same content as above */ }
anchor(FxGravity.CENTER_START, dy = 60f)
systemHost(app) {
permission(FxPermissionStrategy.auto())
fallback(AppHost.builder(app).build())
}
}.show()When a denied permission downgrades it to App Host the container is swapped, but the owner belongs to the control, so the composition state is still kept. The permission strategies live in System Host.
Back to Home · Related: Getting Started Configuration App Host System Host