Status Update
Comments
gr...@google.com <gr...@google.com>
ma...@google.com <ma...@google.com> #2
Adding a parameter for error messages to text fields can result in an unintuitive, less flexible API.
Such a parameter would presumably take a String
, so that it can be passed to Modifier.semantics { error(MESSAGE) }
. However, should this error message automatically be shown as supportingText
? There is an argument to be made either way, with no clear answer.
It is not necessarily the case that the error message announced to semantics needs to be the same that is displayed in supporting text. Rather than trying to guess the exact use case of the developer, the component leaves the decision open-ended.
As you mentioned, you can always clear the semantics of the supporting text in the error state if it duplicates with the message you pass to Modifier.semantics
.
da...@well.co <da...@well.co> #3
Thanks for the reply. So it appears that anyone who wants to do an error has to do everything described in your sample. Consistently check for if the field is inError and if it is then update supporting text, error semantics and whether the textfield is inError. Then also clear semantics optimally on the supporting text. Really great example. Not sure if we can consider putting that in the docs as if you do not do it exactly like that then IMO its not accessible.
It would be great if there was a way to do this more simply and some of the accessibility here came for free but maybe any solution is too presumptuous.
I am not sure how technically this would work implementation but to take your sample and come up with a more simple API
TextField(
state = state,
lineLimits = TextFieldLineLimits.SingleLine,
label = { Text(if (isError) "Username*" else "Username") },
supportingText = {
Text("Limit: ${state.text.length}/$charLimit")
},
errorText = {
Text(errorMessage else "")
},
isError = isError,
onKeyboardAction = { validate(state.text) },
modifier =
Modifier.semantics {
maxTextLength = charLimit
}
)
Where
- errorText composable has all of its descriptions grouped and put into the semantics error if it isError.
- errorText is only displayed on isError
- errorText is automatically clearAndSetSemantics{}
- Where if someone wants to override the semantics error they can manually do it but for free the accessibility comes if someone uses errorText
I am sure there are some interesting edge cases I have not considered but getting the ADA right here is difficult for free.
Description
- When there is a supporting text, and an error occurs, should we show the error instead of the original supporting text? It means that we'll need to have a logic for which text to show.
- When talkback is enabled, and there is a supporting text, tapping on the field reads that supporting text along with other announcements. When there is an error, textfield defaults error semantics to "Invalid input", and in order to update it we need to change error semantics and pass the actual error. Also we update the supporting text to show the actual error. Now tapping on the text field announces both supporting text and error, which results in actual error announced twice. This can be mitigated by clearing the semantics of the supporting text. But we'll need to do that on the error state, because we want supporting text to be announced if it is a normal text.
These complicate error state handling within a text field. There should be a slot for errors and these kinds of logic should happen internally.
Jetpack Compose version:
Compose bom: 2024.12.01
Jetpack Compose component(s) used:
TextField with supporting text, semantics modifier
Android Studio Build:
Android Studio Ladybug | 2024.2.1 Patch 3
Kotlin version:
2.0.0