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
相关产品推荐
相关产品推荐

