如何解决FocusRequester未初始化?

问题描述 投票:0回答:1

我正在处理三个页面:StartPage、LoginPage 和 SignupPage,并且我正在将它们集成到 HorizontalPager 中。

在 SignupPage 中,我在 textField 上使用 requestFocus。

导航流程是“开始”->“登录”,但在转换到“登录页面”时遇到此错误,尽管 focusRequester 是在“注册”上定义的。根据我的研究,发生这种情况的原因是 HorizontalPager,准备下一页,尽管它在屏幕上不可见。

那么我如何仅在 SignupPage 可见时触发 requestFocus,并在 SignupPage 不可见时禁用它。

java.lang.IllegalStateException: 
                                                                                          
FocusRequester is not initialized. Here are some possible fixes:
                                                                                                    
1. Remember the FocusRequester: val focusRequester = remember { FocusRequester() }
2. Did you forget to add a Modifier.focusRequester() ?
3. Are you attempting to request focus during composition? Focus requests should be made in
                                                                                                 
response to some event. Eg Modifier.clickable { focusRequester.requestFocus() }

这是 MyNavigation.kt

@Composable
fun MyAppNavigation(
    modifier: Modifier, authViewModel: AuthViewModel
) {
    val pages = registerInfoList.size
    val pagerState = rememberPagerState(pageCount = { pages })
    val scope = rememberCoroutineScope()

    Column(
        modifier = modifier.fillMaxSize()
    ) {
        HorizontalPager(
            state = pagerState, userScrollEnabled = false
        ) { page ->
            when (page) {
                0 -> StartPage(modifier = modifier,
                    toSignUp = {
                        scope.launch {
                            pagerState.animateScrollToPage(2)
                        }
                    }, toLogin = {
                        scope.launch {
                            pagerState.animateScrollToPage(1)
                        }
                    }, authViewModel = authViewModel
                )


                1 -> LoginPage(modifier = modifier,
                    toHome = {
                        scope.launch {
                            pagerState.animateScrollToPage(3)
                        }
                    }, toSignUp = {
                        scope.launch {
                            pagerState.animateScrollToPage(2)
                        }
                    },
                    toStart = {
                        scope.launch {
                            pagerState.animateScrollToPage(0)
                        }
                    }, authViewModel = authViewModel
                )


                2 -> SignupPage(
                    modifier = modifier,
                    toStart = {
                        scope.launch {
                            pagerState.animateScrollToPage(0)
                        }
                    },
                    toHome = {
                        scope.launch {
                            pagerState.animateScrollToPage(3)
                        }

                    },
                    authViewModel = authViewModel
                )

              ......
            }
        }
    }
}

登录页面.kt

@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class)
@Composable
fun LoginPage(
    modifier: Modifier = Modifier,
    toSignUp: () -> Unit,
    toHome: () -> Unit,
    toStart: () -> Unit,
    authViewModel: AuthViewModel
) {
    ...................

    if (shouldShowDialog.value) {
        AlertDialog(shouldShowDialog = shouldShowDialog, message = errorMessage)
    }
    val authState = authViewModel.authState.observeAsState()

    LocalContext.current
    LaunchedEffect(authState.value) {
        when (authState.value) {
            is AuthState.Authenticated -> toHome()
            is AuthState.Error.InvalidCredentialsError -> {
                errorMessage = (authState.value as AuthState.Error.InvalidCredentialsError).message
                shouldShowDialog.value = true
            }

            is AuthState.Error.NetworkError -> {
                errorMessage = (authState.value as AuthState.Error.NetworkError).message
            }


            else -> Unit
        }
    }
    Scaffold(...................
    ) { innerPadding ->

        Column(
            .......
        ) {
            Text(
                ............
            )
            Spacer(modifier = Modifier.size(4.dp))
            TextField(............)

            Spacer(modifier = Modifier.size(32.dp))
            Text(.............)
            Spacer(modifier = Modifier.size(4.dp))
            TextField(.........)
            Text(
                ..........
            )

            Spacer(modifier = Modifier.height(32.dp))
            var colorButton by remember { mutableStateOf(Color.White) }
            Button(..........)
            Spacer(modifier = Modifier.height(8.dp))

            TextButton(.......)
        }
    }
}

注册页面.kt

@Composable
fun SignupPage(
    modifier: Modifier = Modifier,
    toStart: () -> Unit,
    toHome: () -> Unit,
    authViewModel: AuthViewModel
) {
    .........


    // Added FocusRequesters for each TextField
    val emailFocusRequester = remember { FocusRequester() }
    val passwordFocusRequester = remember { FocusRequester() }
    val userNameFocusRequester = remember { FocusRequester() }
    val keyboardController = LocalSoftwareKeyboardController.current
    val context = LocalContext.current
    LaunchedEffect(authState.value) {
        when (authState.value) {
            is AuthState.Authenticated -> toHome()
            is AuthState.Error.NetworkError -> Toast.makeText(
                context,
                (authState.value as AuthState.Error.NetworkError).message, Toast.LENGTH_SHORT
            ).show()

            else -> Unit
        }
    }
    // Updated LaunchedEffect to handle focus and keyboard visibility

        LaunchedEffect(pagerState.currentPage) {//------> this where the crash happen
            when (pagerState.currentPage) {
                0 -> emailFocusRequester.requestFocus()
                1 -> passwordFocusRequester.requestFocus()
                2 -> keyboardController?.hide()
                3 -> keyboardController?.hide() // No TextField on this page
                4 -> userNameFocusRequester.requestFocus()
            }
            // Delay to ensure the focus change is processed
            delay(100)
            if (pagerState.currentPage in listOf(0, 1, 4)) {
                keyboardController?.show()
            } else {
                keyboardController?.hide()
            }
        }



    Scaffold(
        ..........
    ) { innerPadding ->


        Column(
            ..................
        ) {
            HorizontalPager(
                state = pagerState,
                userScrollEnabled = false
            ) { index ->
                Column(
                    ...............
                ) {
                    Text(
                        ..................
                    )
                    Spacer(modifier = Modifier.size(4.dp))
                    when (index) {
                        0 -> // email and name

                            TextField(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .focusRequester(emailFocusRequester),
                               )

                        1 -> {
                            TextField(modifier = Modifier
                                .fillMaxWidth()
                                .focusRequester(passwordFocusRequester))
                               

                        2 -> {

                            DatePicker { date ->
                                dateOfBirth = date.let {
                                    SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(it)
                                }
                            }
                        }

                        3 -> {
                            ExposedDropdownMenuBox(............)

                        4 -> // email and name

                            TextField(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .focusRequester(userNameFocusRequester),
                                )

                    Spacer(modifier = Modifier.size(4.dp))
                    Text(
                        ....................
                    )
                }
            }
            Spacer(modifier = Modifier.height(16.dp))
            Button(..............)
        }
    }
}

这是我当前的解决方案,不确定这是否符合最佳实践。

val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleState by lifecycleOwner.lifecycle.currentStateFlow.collectAsState()

LaunchedEffect(key1 = lifecycleState, key2 = pagerState.currentPage) {
    when (lifecycleState) {
        Lifecycle.State.DESTROYED -> {
            keyboardController?.hide()
        }

        Lifecycle.State.INITIALIZED -> {
            keyboardController?.hide()
        }

        Lifecycle.State.CREATED -> {
            when (pagerState.currentPage) {
                0 -> emailFocusRequester.requestFocus()
                1 -> passwordFocusRequester.requestFocus()
                2 -> keyboardController?.hide()
                3 -> keyboardController?.hide() // No TextField on this page
                4 -> userNameFocusRequester.requestFocus()
            }
            // Delay to ensure the focus change is processed
            delay(100)
            if (pagerState.currentPage in listOf(0, 1, 4)) {
                keyboardController?.show()
            } else {
                keyboardController?.hide()
            }
        }

        Lifecycle.State.STARTED -> {
            when (pagerState.currentPage) {
                0 -> emailFocusRequester.requestFocus()
                1 -> passwordFocusRequester.requestFocus()
                2 -> keyboardController?.hide()
                3 -> keyboardController?.hide() // No TextField on this page
                4 -> userNameFocusRequester.requestFocus()
            }
            // Delay to ensure the focus change is processed
            delay(100)
            if (pagerState.currentPage in listOf(0, 1, 4)) {
                keyboardController?.show()
            } else {
                keyboardController?.hide()
            }
        }

        Lifecycle.State.RESUMED -> {
            when (pagerState.currentPage) {
                0 -> emailFocusRequester.requestFocus()
                1 -> passwordFocusRequester.requestFocus()
                2 -> keyboardController?.hide()
                3 -> keyboardController?.hide() // No TextField on this page
                4 -> userNameFocusRequester.requestFocus()
            }
            // Delay to ensure the focus change is processed
            delay(100)
            if (pagerState.currentPage in listOf(0, 1, 4)) {
                keyboardController?.show()
            } else {
                keyboardController?.hide()
            }
        }
    }
}
android android-jetpack-compose android-jetpack-navigation android-compose-textfield horizontal-pager
1个回答
0
投票

仅当可组合项组合时才会调用

Modifier.focusRequester(...)
。 等待是相当不精确的。 您可以等待更长时间,也可以只编写如下所示的代码:

val onFocusReady: ()->Unit={}
when(...){
   ...->onFocusReady={someFocusRequester.requestFocus()}
}
when(...){
   ...->TextField(modifier = Modifier
         .fillMaxWidth()
         .focusRequester(passwordFocusRequester))
        onFocusReady()
}

这将确保焦点在您需要时被初始化。 希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.