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

Jetpack Compose预览中如何设置TextField焦点状态用于快照测试?

问题描述

我正在为基于Material TextField的组件生成不同状态的预览,需要用Showkase和Paparazzi做快照测试。现在想创建文本框处于焦点状态的预览,让它渲染出不同颜色的轮廓,但尝试过focusRequester等方法都无效——只有启动交互式预览才有用,但这不适用于Paparazzi。

附上我尝试的代码:

@Preview
@Composable
internal fun TextFieldPreview(
    @PreviewParameter(TextFieldPreviewParameterProvider::class) 
    parameter: TextFieldPreviewParameter
) {
    with(parameter) {
        val focusRequester = remember { FocusRequester() }

        MyTextField(
            value = "Text",
            onValueChange = {},
            isEnabled = isEnabled,
            isPrefilled = isPrefilled,
            isError = isError,
            errorMessage = isError.runIfTrue { "Error message" },
            modifier = Modifier.focusRequester(focusRequester)
        )

        LaunchedEffect(isFocused) {
            if (isFocused){
                focusRequester.requestFocus()
            }
        }
    }
}

internal data class TextFieldPreviewParameter(
    val isFocused: Boolean,
    val isEnabled: Boolean,
    val isPrefilled: Boolean,
    val isError: Boolean,
)
解决方法

静态预览和Paparazzi这类工具不会处理真实的焦点交互事件,所以focusRequester.requestFocus()在非交互式环境下无法触发视觉状态变化。你可以通过以下两种实用方式实现需求:

方法1:自定义交互源模拟焦点状态

通过MutableInteractionSource手动触发焦点交互事件,让TextField感知到焦点状态:

@Preview
@Composable
internal fun TextFieldPreview(
    @PreviewParameter(TextFieldPreviewParameterProvider::class) 
    parameter: TextFieldPreviewParameter
) {
    with(parameter) {
        val interactionSource = remember { MutableInteractionSource() }

        // 根据参数模拟焦点/失焦交互
        LaunchedEffect(isFocused) {
            if (isFocused) {
                interactionSource.emit(Focus.InteractionFocus())
            } else {
                interactionSource.emit(Focus.InteractionUnfocus())
            }
        }

        MyTextField(
            value = "Text",
            onValueChange = {},
            isEnabled = isEnabled,
            isPrefilled = isPrefilled,
            isError = isError,
            errorMessage = isError.runIfTrue { "Error message" },
            // 将自定义交互源传递给TextField
            interactionSource = interactionSource
        )
    }
}

注意:需要确保你的MyTextField封装组件会将interactionSource参数传递给底层的Material TextField,否则这个方法不会生效。

方法2:直接绑定轮廓颜色到焦点参数

如果只需要在预览中展示焦点状态的视觉效果,可以绕过焦点逻辑,直接根据isFocused参数设置轮廓颜色:

// 先修改MyTextField,支持根据状态自定义轮廓颜色
@Composable
fun MyTextField(
    value: String,
    onValueChange: (String) -> Unit,
    isEnabled: Boolean,
    isPrefilled: Boolean,
    isError: Boolean,
    errorMessage: String?,
    isFocused: Boolean, // 添加焦点状态参数
    modifier: Modifier = Modifier
) {
    val borderColors = TextFieldDefaults.outlinedTextFieldColors(
        focusedBorderColor = when {
            isError -> Color.Red
            isFocused -> Color.Blue // 焦点状态的轮廓色
            else -> TextFieldDefaults.outlinedTextFieldColors().focusedBorderColor
        },
        unfocusedBorderColor = when {
            isError -> Color.Red
            !isEnabled -> Color.Gray
            else -> TextFieldDefaults.outlinedTextFieldColors().unfocusedBorderColor
        }
    )

    TextField(
        value = value,
        onValueChange = onValueChange,
        enabled = isEnabled,
        isError = isError,
        colors = borderColors,
        modifier = modifier
        // 其他原有参数...
    )
}

// 预览中直接传递isFocused参数
@Preview
@Composable
internal fun TextFieldPreview(
    @PreviewParameter(TextFieldPreviewParameterProvider::class) 
    parameter: TextFieldPreviewParameter
) {
    with(parameter) {
        MyTextField(
            value = "Text",
            onValueChange = {},
            isEnabled = isEnabled,
            isPrefilled = isPrefilled,
            isError = isError,
            errorMessage = isError.runIfTrue { "Error message" },
            isFocused = isFocused // 直接传递焦点状态
        )
    }
}

这种方法更直接,不需要依赖交互事件,完全通过参数控制视觉表现,适配静态预览和Paparazzi的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 13:00:49