Skip to content

ScrollToTopButton — 回到顶部 ​

长列表右下角那个圆形箭头按钮。全站唯一实现。

基本用法 ​

显示与否交给调用方 —— 本组件只管长什么样:

kotlin
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()

Box(Modifier.fillMaxSize()) {
    LazyColumn(state = listState) { /* … */ }

    AnimatedVisibility(
        visible = listState.canScrollBackward,
        enter = fadeIn(),
        exit = fadeOut(),
        modifier = Modifier
            .align(Alignment.BottomEnd)
            .padding(end = 16.dp, bottom = 24.dp),
    ) {
        ScrollToTopButton(
            onClick = { scope.launch { listState.animateScrollToItem(0) } }
        )
    }
}

参数 ​

参数类型默认值说明
onClick() -> Unit—点击回调。滚动动作由调用方实现,组件不持有任何滚动状态
modifierModifierModifier修饰符。定位与间距都在这里给(align / padding)
colorsScrollToTopButtonColorsScrollToTopButtonDefaults.colors()配色

ScrollToTopButtonColors ​

字段默认值说明
containerColorMaterialTheme.colorScheme.primary圆形底色
contentColorMaterialTheme.colorScheme.onPrimary箭头颜色
kotlin
ScrollToTopButton(
    onClick = { /* … */ },
    colors = ScrollToTopButtonDefaults.colors(
        containerColor = MaterialTheme.colorScheme.inverseSurface,
    ),
)

要点 ​

  • 显隐由调用方判断。 组件内部不会自己决定「该不该出现」。用 listState.canScrollBackward 配 AnimatedVisibility,别用 firstVisibleItemIndex > 0 —— 后者在「刚滚一点点又滚回来」时会来回抖。
  • 尺寸固定 52dp,别覆写。 它是悬浮在内容之上的圆形按钮,modifier.size() 会破掉圆形比例和箭头的内留白。
  • 图标是 Phosphor.ArrowUp(22dp)。 别换方向 —— 「回到顶部」用上箭头,不是表示「返回」的左箭头。
  • 触控区就是这个 52dp 的圆,已经超过 48dp 的最小标准,不需要再包一层 IconButton。
  • 它没有阴影(本体是 Box + background + clip,不带 elevation)。想让它更浮起来,自己在 modifier 上补 shadow()。

说明 ​

  • 为什么统一成实心近黑圆形:以前首页和搜索页各写了一个 —— 首页是黑色圆形 + 淡入,搜索页是浅灰药丸 + FloatingActionButton 的缩放动画。同一个动作在相邻两页长得不一样,用户会以为是两个不同的东西。现在统一成实心近黑圆形:它是无风险的导航动作,不需要抢视线,但必须一眼可辨;用主色(近黑)保证在任何表面色上都够醒目。
  • 为什么不用 FloatingActionButton:FAB 自带 elevation 和「强调主操作」的语义,而「回到顶部」不是主操作。这里用裸 Box + clickable,不带阴影、也不受 FAB 的尺寸约束。
  • 和 AnimatedVisibility 的分工:滚动监听、显隐动画、位置全在调用方;组件只保证「长什么样」在任何页面都一致。