You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jetpack Compose中实现自定义Toast?

Jetpack Compose 自定义Toast实现方案

方案1:封装原生Toast实现自定义样式

该方案兼容系统Toast的全局显示逻辑,不需要依赖当前Compose页面的生命周期,适合需要跨页面显示的场景。
实现代码如下:

fun Context.showCustomToast(
    message: String,
    duration: Int = Toast.LENGTH_SHORT,
    icon: Int? = null
) {
    val toast = Toast.makeText(this, "", duration)
    // 用ComposeView渲染自定义样式,无需编写xml布局
    val composeView = ComposeView(this).apply {
        setContent {
            Surface(
                shape = RoundedCornerShape(8.dp),
                color = Color(0xCC000000),
                shadowElevation = 4.dp
            ) {
                Row(
                    modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    horizontalArrangement = Arrangement.spacedBy(8.dp)
                ) {
                    icon?.let {
                        Icon(
                            painter = painterResource(id = it),
                            contentDescription = null,
                            tint = Color.White,
                            modifier = Modifier.size(20.dp)
                        )
                    }
                    Text(
                        text = message,
                        color = Color.White,
                        fontSize = 14.sp
                    )
                }
            }
        }
    }
    toast.view = composeView
    // 自定义Toast显示位置,距离底部120dp
    toast.setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, 120.dp.roundToPx())
    toast.show()
}

在Composable中调用方式:

val context = LocalContext.current
Button(onClick = {
    context.showCustomToast(
        message = "操作成功",
        icon = R.drawable.ic_success,
        duration = Toast.LENGTH_SHORT
    )
}) {
    Text("显示Toast")
}

注意事项:

  • Android 11及以上系统禁止了自定义Toast的后台显示,仅前台显示不受影响
  • 若需要适配更高版本系统限制,推荐使用第二种纯Compose实现方案

方案2:纯Compose实现的页面内自定义Toast

该方案完全运行在Compose树内,不受系统版本限制,样式自定义自由度更高,可以实现动画、手势交互等复杂效果。
实现思路:用CompositionLocal提供全局的Toast触发方法,在根布局嵌套Toast悬浮层,通过状态控制显示隐藏。

步骤1:定义Toast数据类和状态控制器

data class ToastState(
    val message: String,
    val duration: Long = 2000L,
    val icon: Int? = null
)

class ToastHostState : androidx.compose.runtime.SaveableStateHolder {
    val currentToast = mutableStateOf<ToastState?>(null)
    private var toastJob: Job? = null

    fun showToast(toast: ToastState) {
        toastJob?.cancel()
        currentToast.value = toast
        toastJob = CoroutineScope(Dispatchers.Main).launch {
            delay(toast.duration)
            currentToast.value = null
        }
    }

    override fun saveState(): Bundle? = null
    override fun restoreState(savedState: Bundle?) = Unit
    override fun <T : Any> removeState(key: Any) = Unit
    override fun <T : Any> consumeSavedState(key: Any): T? = null
}

步骤2:定义CompositionLocal传递全局状态

val LocalToastHostState = staticCompositionLocalOf<ToastHostState> {
    error("No ToastHostState provided, please init at root layout")
}

步骤3:实现ToastHost悬浮层组件

@Composable
fun ToastHost(
    hostState: ToastHostState,
    modifier: Modifier = Modifier
) {
    val toast = hostState.currentToast.value
    // 自带显隐动画
    AnimatedVisibility(
        visible = toast != null,
        modifier = modifier.fillMaxSize(),
        enter = fadeIn() + slideInVertically(initialOffsetY = { it / 2 }),
        exit = fadeOut() + slideOutVertically(targetOffsetY = { it / 2 })
    ) {
        toast?.let {
            Box(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(bottom = 120.dp),
                contentAlignment = Alignment.BottomCenter
            ) {
                Surface(
                    shape = RoundedCornerShape(8.dp),
                    color = Color(0xCC000000),
                    shadowElevation = 4.dp
                ) {
                    Row(
                        modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        toast.icon?.let { iconRes ->
                            Icon(
                                painter = painterResource(id = iconRes),
                                contentDescription = null,
                                tint = Color.White,
                                modifier = Modifier.size(20.dp)
                            )
                        }
                        Text(
                            text = toast.message,
                            color = Color.White,
                            fontSize = 14.sp
                        )
                    }
                }
            }
        }
    }
}

步骤4:根布局注入全局状态

class MainActivity : ComponentActivity() {
    private val toastHostState = ToastHostState()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CompositionLocalProvider(LocalToastHostState provides toastHostState) {
                YourAppTheme {
                    Box(modifier = Modifier.fillMaxSize()) {
                        // 应用主内容,比如NavHost
                        AppNavGraph()
                        // Toast层放在最上层,保证不被其他内容遮挡
                        ToastHost(hostState = toastHostState)
                    }
                }
            }
        }
    }
}

步骤5:任意子Composable中调用

val toastHostState = LocalToastHostState.current
Button(onClick = {
    toastHostState.showToast(
        ToastState(
            message = "纯Compose Toast测试",
            icon = R.drawable.ic_tip,
            duration = 3000L
        )
    )
}) {
    Text("显示自定义Toast")
}

注意事项:

  • 该方案的Toast仅在当前Compose树内显示,退出当前页面会自动消失
  • 可根据需求自定义动画、位置、交互逻辑,比如支持点击关闭、上滑消失等效果

内容的提问来源于stack exchange,提问作者ABINASH KUMAR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 16:15:03