Hi All, so i’ve wrapped a RadioButton in a Row wit...
# compose
m
Hi All, so i’ve wrapped a RadioButton in a Row with some text. I then put the selectable modifier around that. I can now click anywhere in the row to select that radio button. However, when using the screenreader it initially announces it as i would like. But then if i swipe right, it’s individually selecting the radio button which i don’t want. Is there any way to disable that? (code in thread)
Copy code
Row(
        modifier = modifier.selectable(
            selected = selected,
            onClick = onClick,
            enabled = enabled,
            role = role,
            interactionSource = interactionSource,
            indication = LocalIndication.current
        ),
        verticalAlignment = Alignment.CenterVertically
    ) {
       // Text and Radio Button
    }
I’ve tried adding .focusable(enabled=false) to the actual radio button itself, and that seems to have no effect.
z
I think you want to merge semantics, so
Modifer.semantics(mergeDescendents=true)
c
Interesting. I would have thought the selectable modifier would merge automatically. I gotta go update some code this means! Good catch @mattinger
m
@Zach Klippenstein (he/him) [MOD] I tried adding the merge semantics to the Row above, and it doesn't seem to change anything
The tree is still showing them as separate semantics:
Copy code
Printing with useUnmergedTree = 'false'
Node #1 at (l=0.0, t=56.0, r=41.0, b=93.0)px
 |-Node #2 at (l=0.0, t=56.0, r=41.0, b=93.0)px
   Role = 'RadioButton'
   Selected = 'false'
   Text = '[Label]'
   Actions = [OnClick, GetTextLayoutResult]
   MergeDescendants = 'true'
    |-Node #5 at (l=0.0, t=61.0, r=28.0, b=89.0)px, Tag: 'button1'
      Tone = 'Neutral'
      Role = 'RadioButton'
      Selected = 'false'
      Actions = [OnClick]
      MergeDescendants = 'true'
I find it strange that even though i deliberately set the mergeDescendants flag on the radio button itself to "false" it's coming back as true. Is there something about the "selectable" modifier that maybe does that?
looks like through a series of calls, it’s ending up calling:
Copy code
genericClickableWithoutGesture
when you use .selectable
that is setting the merge flag to true on the radio button
and from what i can tell from the documentation, it won’t merge nodes that both have mergeDecendants set to true
z
Hm, good point.
@Anastasia [G] Do you know what the right thing to do is here?
a
Are you specifying the
onClick
parameter of
RadioButton
? If so, try setting it to null.
m
let me try that.
that’s perfect @Albert Chang. I’ll have to see if i can make the same trick work with switch and checkbox (we’re doing this with all 3 of them)
So it worked with switch, but checkboxes are wierd. It announces as: Not Checked, Not Checked, Label Checkbox, Double Tap to Toggle
c
Oh nice. No need for mergeDescendants? Just onClick = null?
m
Yes. Other than the checkbox which is announcing Not checked twice.
c
Seems like maybe thats worth filing a bug report.
m
It’s possible because we’re doing a triStateToggleable for the checkbox. Checkboxes on their own announce fine.
c
There's a tristate toggleable for a checkbox? I always thought there were two states. checked and unchecked. mind blown
m
Copy code
@Composable
fun TriStateCheckbox(
    state: ToggleableState,
    onClick: (() -> Unit)?,
    modifier: Modifier = Modifier,
    enabled: Boolean = true,
    interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
    colors: CheckboxColors = CheckboxDefaults.colors()
)
it has an indeteriminate state which puts a “-” in there. It’s good for hierarchal things where some underneath it may be checked and some not.
we’ve just based our checkbox off of that, but it seems like nesting these things is having issues.
I’ve even validated that a null is being passed to the triStateToggleable in the underlying code:
Copy code
Modifier.selectable(
                selected = selected,
                onClick = onClick,
                enabled = enabled,
                role = Role.RadioButton,
                interactionSource = interactionSource,
                indication = rememberRipple(
                    bounded = false,
                    radius = RadioButtonRippleRadius
                )
            )
Switch is still an interesting case, because you can no longer swipe it to change it’s state. you can only tap
a
Yeah that's expected. You can check the expected behavior in the system setting app.
m
I figured out the checkbox thing. we add some additional semantic properties and an overzealous dev added role and toggleableState when they’re already taken care of in the TriStateCheckbox.
a
The suggestion above to pass
null
is right, this is exactly why we made the callback nullable. We keep https://issuetracker.google.com/issues/193414941 open to improve the API reference documentation, maybe a sample of this use case closer to the
RadioButton
documentation itself would help
m
Thanks @Anastasia [G] for the link.