Status Update
Comments
ma...@google.com <ma...@google.com> #2
Thanks Alex.
The reason they are non null is because we decided before that this is ok to provide states for both Row(Modifier.selectable(...))
and a RadioButton
and both of them being visible for a11y as well is ok and on par with android if I recall correctly. Also we want to encourage the behaviour where you can tap both Row and RadioButton and get different ripple effect (big bounded ripple for Row and small unbounded for the RadioButton).
Having null
onClick is not an easy design change since it might imply to people that passing a nullable onClick should disable the RadioButton and resulting with different UI.
I will doublecheck with UX team if we should make it possible to ripple on the RadioButton only when you tap the Row
itself, but IMO in this case it will go against the nature of the ripple being a responsive tap indicator under your finger.
Stay tuned :)
al...@gmail.com <al...@gmail.com> #3
The reason they are non null is because we decided before that this is ok to provide states for both Row(Modifier.selectable(...)) and a RadioButton and both of them being visible for a11y as well is ok and on par with android if I recall correctly.
Hmm, interesting! The reason why I filed this bug actually was because I was trying to exactly replicate the behavior of android.widget.RadioButton
.
-
When using
android.widget.RadioButton
, it will never show a ripple for the entire row. Even if you click on theRadioButton
s text, it will only show a ripple on top of theRadioButton
icon. (See theradiobutton-ripple.mp4
video below, which shows the behavior of aandroid.widget.RadioButton
view in a non-Compose app). -
When using
android.widget.RadioButton
w/ TalkBack, it will focus the entire thing (the radio button icon and the text). When swiping through the screen, the individual radio button icon won't ever gain focus individually. (See theradiobutton-talkback.mp4
video below, which shows the behavior of aandroid.widget.RadioButton
view in a non-Compose app).
My main goal here is to create a Compose layout that is exactly identical to a layout you would otherwise create with traditional views, because I don't want there to be any UI inconsistencies between compose widgets and traditional views in our existing app. So perhaps that is why UX may feel differently about the behavior. :)
Either way, I'll leave it up to you to decide what is best for the material library (I'm not actually affected by this, as I've created my own custom RadioButton
for my own design system library, but figured I should file the bug just in case anyways!)
al...@gmail.com <al...@gmail.com> #4
OK, reporting back on this... I actually found out a way to do this w/o the need for a nullable click listener.
TIL you can hide things from talkback with Modifier.semantics { hidden() }
:)
Please feel free to close this bug!
br...@monzo.com <br...@monzo.com> #5
FWIW I also want non-interactive versions of these components.
My use case is to have a ListItem
with a trailing Checkbox
where clicking the list item toggles the checked state. I’m currently sharing the interactionState
between the checkbox and list item so that they ripple together. Sharing interactions like this is cool, but I’d prefer having no ripple at all on the checkbox.
There's a workaround where you can wrap the built in component, disable the component, and change the disabled colours, i.e.:
@Composable
fun ReadOnlySwitch(checked: Boolean) {
val colors = SwitchConstants.defaultColors(
disabledCheckedThumbColor = MaterialTheme.colors.secondaryVariant,
disabledCheckedTrackColor = MaterialTheme.colors.secondaryVariant,
disabledUncheckedThumbColor = MaterialTheme.colors.surface,
disabledUncheckedTrackColor = MaterialTheme.colors.onSurface
)
Switch(checked, onCheckedChange = {}, enabled = false, colors = colors)
}
However this solution isn't as obvious as passing a null onCheckedChange
or something. I wonder if the APIs can be improved a bit to make these use cases more intuitive?
al...@gmail.com <al...@gmail.com> #6
Edit: yeah, actually I take it back about closing this bug. Agree with the issue discussed above.
p.s. I was talking to Louis about this issue over Slack and he was the one who asked me to file this bug, so he'd have more context if you need more information.
ae...@google.com <ae...@google.com>
ap...@google.com <ap...@google.com> #7
Branch: androidx-main
commit a234d18045d35a211dfceea56b20fece51dc34cb
Author: Alexandre Elias <aelias@google.com>
Date: Tue Feb 09 14:10:13 2021
Improve selection control APIs to support "passive embedding"
This makes the Switch/Checkbox/RadioButton lambdas nullable. If null,
then the entire "toggleable" modifier is removed, disabling click input
handling, Material ripple and accessibility semantics.
These are intended for use cases (as in the samples) where the control
is embedded within a larger row which handles the click action for it.
Note that this is distinct from "enabled", which is for use cases where
the UI is grayed out to indicate it cannot currently be interacted with
in any way.
Bug: 171819073
Test: "New 'whenNullLambda' tests in RadioButtonTest,
SwitchTest, and CheckboxUiTest"
Relnote: "Switch, Checkbox and RadioButton action lambdas are now
nullable. Checkbox-in-clickable-row samples updated to use this
feature."
Change-Id: If601b88cf4622111bca5f4927cbb86c7d300ebbf
M compose/material/material/api/current.txt
M compose/material/material/api/public_plus_experimental_current.txt
M compose/material/material/api/restricted_current.txt
M compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
M compose/material/material/samples/src/main/java/androidx/compose/material/samples/SelectionControlsSamples.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/CheckboxUiTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/RadioButtonTest.kt
M compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/SwitchTest.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Checkbox.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/RadioButton.kt
M compose/material/material/src/commonMain/kotlin/androidx/compose/material/Switch.kt
ae...@google.com <ae...@google.com> #8
OK, after some discussion, we decided the suggested API change was a good one and landed it :)
Re:
Re: the radiobutton ripple videos in #3. In the latest Modifier.clearAndSetSemantics {}
around a radio-button-with-identical-non-nullable-lambda will continue to be a supported way of doing things.
Description
Consider a custom
ListItem
component that displays aRadioButton
at the start of its content, implemented something like this:The problem I have with this code is that I only want the
Row
to show up as focusable in TalkBack and Ionly
want the Row to have a ripple indication. In other words, I would like theRadioButton
to be treated just as a decorative icon inside theRow
that is not individually interactive at all.The problem with this code is that by passing
onClick
to both theRow
and theRadioButton
, both end up being interactive and clickable. However,RadioButton
requires the click listener to be non-null, so there is currently no way to workaround this using the current API.The only way that I’ve been able to workaround this is by creating a custom
RadioButton
component that accepts a nullableonClick
argument. This allows me to passnull
as the click listener in the code above, and internally theRadioButton
will do something likeif (onClick == null) Modifier else Modifier.selectable(...)
to ensure that it can be used as a non-interactive widget.This same issue also exists for
Checkbox
andSwitch
as well.