Skip to content

Compose 浮窗

petterp edited this page Aug 30, 2026 · 1 revision

Compose

floatingx-compose 让浮窗内容直接是一段组合:compose { control -> … }。 组合跑在浮窗自己的 FxComposeOwner 上,所以 viewModel() / rememberSaveable 的状态不随宿主 Activity 销毁而清空。模块 minSdk 23,见 快速开始

用法

FloatingX.install("compose") {
    compose { control ->
        val vm: CounterViewModel = viewModel()                        // 归浮窗自己的 ViewModelStore
        var count by rememberSaveable { mutableIntStateOf(0) }        // 容器 detach 也不丢
        val state by control.stateFlow().collectAsState()             // FxState
        val pos by control.positionFlow().collectAsState()            // 内容左上角的屏幕坐标
        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 {} 只是设置内容,其余配置项(锚点 / 吸附 / 手势 / 动画 / 持久化)与 View 内容完全一样, 见 配置项

FxComposeOwner:状态跨页存活

每个浮窗自带一个 FxComposeOwner,同时是 LifecycleOwner / ViewModelStoreOwner / SavedStateRegistryOwner,归 control 所有(不归宿主 Activity)。它的生命周期跟着浮窗走:

浮窗动作 owner 的 Lifecycle.State
attach(容器挂上宿主) STARTED
show RESUMED
detach(换页、被黑名单卸下、旋转重建) CREATED
cancel() DESTROYED(终态)

只有 cancel() 才会 DESTROYED,所以换页、旋转、被黑名单卸下再回来,整棵组合的状态都不重来 (#210/#239)。

  • viewModel() 落在 owner 的 ViewModelStore,存活范围是 install ~ cancel,与页面无关。
  • rememberSaveable 走浮窗自己的过桥仓库:组合被 dispose(容器 detach)时存进去,重新组合时取回。

compose {} 里的 rememberSaveable 能跨进程恢复吗? 不能。它只在进程内保存:组合被 dispose(容器 detach)时存进浮窗自己的过桥仓库, 重新组合时取回;进程被杀就没了(浮窗本身也不会自动重建)。要跨进程恢复请自己持久化。

stateFlow / positionFlow

public fun FxControl.stateFlow(): StateFlow<FxState>
public fun FxControl.positionFlow(): StateFlow<FxPoint>
  • stateFlow() —— 浮窗状态(INSTALLED / ATTACHED / SHOWN / CANCELLED)。
  • positionFlow() —— 内容左上角的屏幕坐标(等同 control.position,拖动中每帧更新、 moveTo 与吸附结束时更新)。注意与 FxListener.onDrag/onDragEnd 回调里的 x/y 不同,那两个是 相对容器的坐标。
  • 两者都必须在主线程调用,flow 也只在主线程更新;同一个 control 多次调用返回同一条 flow。
  • cancel() 之后 core 会清空 listeners,flow 停在最后一个值上,不再更新。

对 host 无感知

compose {} 不关心浮窗挂在哪,App 级与系统级写法完全一样,换掉 host 那几行即可:

FloatingX.install("compose-sys") {
    compose { control -> /* 与上面完全相同的内容 */ }
    anchor(FxGravity.CENTER_START, dy = 60f)
    systemHost(app) {
        permission(FxPermissionStrategy.auto())
        fallback(AppHost.builder(app).build())
    }
}.show()

权限被拒降级到 App 级全局浮窗 时容器换掉,但 owner 归 control 所有,组合状态照样保留。 系统浮窗的权限策略见 系统级浮窗


返回 Home · 相关:快速开始 配置项 App 级全局浮窗 系统级浮窗

Clone this wiki locally