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

Jetpack Compose UI测试出现FocusRequester未初始化IllegalStateException如何解决

问题原因
  • 该报错是测试环境和正式运行环境的Compose渲染时序差异导致的:正式环境中,FocusRequester通过Modifier.focusRequester()绑定到TextField可聚焦节点的时序,早于LaunchedEffect的执行时机,因此请求焦点能正常触发。而测试环境中LaunchedEffect的执行时机更早,会在FocusRequester完成绑定前就调用requestFocus(),此时FocusRequester还未关联到任何可聚焦节点,就会抛出未初始化的异常。
  • 另外Compose UI测试默认关闭了部分窗口焦点自动获取逻辑,也会加剧该问题的出现概率。
解决方案

以下三种方案均可解决问题,可根据场景选择:

方案1:修改业务代码,等待布局完成后再请求焦点

在LaunchedEffect中先等待一帧布局完成,再发起焦点请求,修改后代码如下:

@Composable
fun ScreenView(
    data: ScreenViewData,
) {
    val focusRequester = remember { FocusRequester() }

    LaunchedEffect(key1 = Unit) {
        // 等待一帧,确保Modifier绑定完成,也可替换为delay(10) 实现相同效果
        awaitFrame()
        focusRequester.requestFocus()
    }

    Scaffold() { innerPadding ->
        Column {
            OutlinedTextField(
                modifier = Modifier.focusRequester(focusRequester),
                value = "",
                onValueChange = {}
            )
        }
    }
}

注:awaitFrame() 需要引入依赖 androidx.compose.ui:ui-platform

方案2:修改业务代码,布局完成回调触发焦点请求

通过onGloballyPositioned监听TextField的布局完成事件,只有控件成功渲染到视图树后才触发焦点请求,稳定性更高:

@Composable
fun ScreenView(
    data: ScreenViewData,
) {
    val focusRequester = remember { FocusRequester() }
    var hasRequestedFocus by remember { mutableStateOf(false) }

    Scaffold() { innerPadding ->
        Column {
            OutlinedTextField(
                modifier = Modifier
                    .focusRequester(focusRequester)
                    .onGloballyPositioned {
                        if (!hasRequestedFocus) {
                            focusRequester.requestFocus()
                            hasRequestedFocus = true
                        }
                    },
                value = "",
                onValueChange = {}
            )
        }
    }
}

方案3:仅修改测试代码,无需改动业务逻辑

在测试的setContent执行后,先等待UI完全渲染空闲,再执行后续断言:

@ExperimentalAnimationApi
class ScreenViewTest {

    @get:Rule
    val composeTestRule = createComposeRule()

    @ExperimentalMaterialApi
    @Test
    fun ScreenViewElementsAreDisplayed() {
        composeTestRule.setContent {
            MyAppTheme {
                ScreenView(
                    data = ScreenViewData(),
                )
            }
        }
        // 等待UI合成、布局全部完成
        composeTestRule.waitForIdle()
        // 后续断言逻辑
    }
}

如果上述方案还是报错,可以在测试代码中额外给根布局请求一次焦点:

composeTestRule.onRoot().performSemanticsAction(SemanticsActions.RequestFocus)
composeTestRule.waitForIdle()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 01:15:03