FloatingActionButton:突出动作与滚动响应 · AndroidX 源码指南
AAndroidX 源码指南
M3E 设计系统
ANDROIDX / COMPOSE / MATERIAL 3

FloatingActionButton:突出动作与滚动响应

选择 FAB 尺寸、实现 Extended 展开收起,并用 MotionScheme 驱动显示隐藏。

FAB 不是普通圆形按钮

AndroidX KDoc 对 FAB 的定位非常明确:它代表屏幕上最重要的动作。一个页面放多个互相竞争的 FAB,会破坏这层信息优先级。

四种尺寸入口

  • SmallFloatingActionButton
  • FloatingActionButton
  • MediumFloatingActionButton
  • LargeFloatingActionButton

它们不仅是同一个按钮随意 size()。源码分别从 small、default、medium、large token 读取最小容器尺寸和默认 shape,图标尺寸也有对应的 Defaults 属性。

MediumFloatingActionButton(onClick = { /* create */ }) {
    Icon(
        Icons.Filled.Add,
        contentDescription = "新建",
        modifier = Modifier.size(FloatingActionButtonDefaults.MediumIconSize),
    )
}

随滚动显示和隐藏

源码提供了专用 modifier:

val visible by remember {
    derivedStateOf { listState.firstVisibleItemIndex == 0 }
}

MediumFloatingActionButton(
    modifier = Modifier.animateFloatingActionButton(
        visible = visible,
        alignment = Alignment.BottomEnd,
    ),
    onClick = {},
) {
    Icon(Icons.Filled.Add, contentDescription = "新建")
}

如果没有自定义 animation spec,实现默认使用:

  • 缩放:MotionScheme 的 Fast Spatial
  • 透明度:MotionScheme 的 Fast Effects

这正是主题级 MotionScheme 能发挥作用的地方:调用方只表达 visible,组件负责选择统一的运动规格。

Extended FAB 是展开,不只是加文字

ExtendedFloatingActionButton 有接收 expanded 的重载。AndroidX sample 根据列表滚动位置切换 expanded,在紧凑状态只保留图标:

ExtendedFloatingActionButton(
    expanded = expandedFab,
    onClick = {},
    icon = { Icon(Icons.Filled.Add, contentDescription = "新建") },
    text = { Text("新建项目") },
)

折叠后文字不可见,因此图标的 contentDescription 和 Tooltip 不能省略。官方 FAB 动画 sample 也明确包裹了 TooltipBox

迁移建议

如果已有页面使用普通 FloatingActionButton

  1. 先确认它确实是页面唯一的主要动作。
  2. 根据视觉层级选择尺寸,而不是直接放大 Modifier。
  3. 滚动页面再考虑 animateFloatingActionButton
  4. 只有文字能显著降低歧义时才采用 Extended。

复制即用

随滚动显示隐藏的 FAB:

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Icon
import androidx.compose.material3.MediumFloatingActionButton
import androidx.compose.material3.animateFloatingActionButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

@Composable
fun ScrollAwareFab() {
    val listState = rememberLazyListState()
    val visible by remember {
        derivedStateOf { listState.firstVisibleItemIndex == 0 }
    }

    LazyColumn(state = listState) {
        items(50) { index -> /* item */ }
    }

    MediumFloatingActionButton(
        modifier = Modifier.animateFloatingActionButton(
            visible = visible,
            alignment = Alignment.BottomEnd,
        ),
        onClick = {},
    ) {
        Icon(Icons.Filled.Add, contentDescription = "新建")
    }
}

动画默认使用主题 MotionScheme 的 Fast Spatial/Fast Effects;不要在一个页面放多个 FAB。