Despite all the examples here, even the hacky solutions using a delay, nothing seems to work.
The focus needs to happen on recomposition when an error flag is true validating a username or a password. These errors work and display correctly. what doesn't work is requesting focus which subsequently does not bring the element into the view.
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun RegistrationScreenExample(
errorState: List<RegistrationErrorState> = listOf(RegistrationErrorState(false)),
enableContinue: Boolean = LocalInspectionMode.current,
onContinue: (String, String) -> Unit,
usernameValidation: UsernameValidation,
passwordValidation: PasswordValidation,
previewUsername: String = if (LocalInspectionMode.current) "[email protected]" else "",
previewPassword: String = if (LocalInspectionMode.current) "testPassword" else "",
) {
val focusRequester = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
val bringIntoViewRequester = remember { BringIntoViewRequester() }
val coroutineScope = rememberCoroutineScope()
val focusModifier = Modifier
.focusable()
.focusRequester(focusRequester)
.onFocusEvent {
if (it.isFocused) {
coroutineScope.launch {
bringIntoViewRequester.bringIntoView()
}
}
}
var username by remember { mutableStateOf(previewUsername) }
var password by remember { mutableStateOf(previewPassword) }
var passwordConfirmation by remember { mutableStateOf(previewPassword) }
val usernameError: RegistrationErrorState? = errorState.firstOrNull { it.errorField == RegistrationFieldEnum.USERNAME }
val passwordError: RegistrationErrorState? = errorState.firstOrNull { it.errorField == RegistrationFieldEnum.PASSWORD }
val passwordConfirmationError: RegistrationErrorState? = errorState.firstOrNull { it.errorField == RegistrationFieldEnum.PASSWORD_CONF }
LaunchedEffect(Unit) {
if (usernameError?.isError == true || passwordError?.isError == true) {
// Doesnt matter how long, never works.
delay(1000.toLong())
focusRequester.requestFocus()
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
item {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 32.dp),
) {
val userModifier = if(usernameError?.isError == true) focusModifier else Modifier
// Username
Input(
modifier = userModifier,
label = "Username",
value = username,
isError = usernameError?.isError == true,
supportingText = usernameError?.errorMessage ?: "",
onValueChange = {
usernameValidation.username = it
username = it
}
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
RequirementIndicator(
visible = true,
requirementMet = usernameValidation.lengthValid,
requirementDescription = "Length 6-36"
)
RequirementIndicator(
visible = true,
requirementMet = usernameValidation.legalCharacters,
requirementDescription = "Special '.' and '@' allowed"
)
}
StandardSpacer()
val passModifier = if(usernameError?.isError == true) focusModifier else Modifier
// Password
Input(
modifier = passModifier,
label = "Create Password",
value = password,
onValueChange = {
passwordValidation.password = it
password = it
},
)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp)
) {
RequirementIndicator(
visible = true,
requirementMet = passwordValidation.lengthValid,
requirementDescription = "Length 8-64"
)
RequirementIndicator(
visible = true,
requirementMet = passwordValidation.hasUppercase,
requirementDescription = "At least 1 Uppercase"
)
RequirementIndicator(
visible = true,
requirementMet = passwordValidation.hasLowercase,
requirementDescription = "At least 1 Lowercase"
)
RequirementIndicator(
visible = true,
requirementMet = passwordValidation.hasNumber,
requirementDescription = "At least 1 Number"
)
RequirementIndicator(
visible = true,
requirementMet = passwordValidation.hasSpecialCharacter,
requirementDescription = "At least 1 Special Character"
)
}
StandardSpacer()
// Password Confirmation
Input(
label = "Confirm Password",
value = passwordConfirmation,
isError = passwordConfirmationError?.isError == true,
onValueChange = {
passwordConfirmation = it
},
supportingText = if (passwordConfirmation.isNotEmpty()) passwordConfirmationError?.errorMessage
?: "" else "",
)
StandardSpacer()
}
}
item {
OutlinedButton(
modifier = Modifier
.fillMaxWidth(.4f)
.padding(bottom = 32.dp),
enabled = enableContinue,
onClick = {
// Clearing focus works just fine
focusManager.clearFocus()
onContinue(username, password)
},
) {
Text(text = "Continue")
}
}
}
}