I followed the checkbox example on Litho guide and built a similar implementation for a radio button:
@LayoutSpec
object CRadioSpec {
@OnCreateLayout
fun onCreateLayout(
c: ComponentContext,
@Prop value: String,
@State isChecked: Boolean
): Component? {
return Column.create(c)
.alignContent(YogaAlign.CENTER)
.widthDip(20F)
.child(
Image.create(c)
.drawableRes(
if (isChecked) R.drawable.ic_checkbox_on
else R.drawable.ic_checkbox_off)
.clickHandler(CRadio.onCheckboxClicked(c))
)
.child(
Text.create(c)
.alignment(TextAlignment.CENTER)
.alignSelf(YogaAlign.CENTER)
.widthDip(20F)
.text(value)
.build()
)
.build()
}
@OnCreateInitialState
fun onCreateInitialState(
c: ComponentContext?,
isChecked: StateValue<Boolean?>,
@Prop initChecked: Boolean
) {
isChecked.set(initChecked)
}
@OnUpdateState
fun updateCheckboxState(isChecked: StateValue<Boolean?>) {
isChecked.get()?.let {
isChecked.set(it.not())
}
}
@OnUpdateState
fun updateCheckbox(isChecked: StateValue<Boolean?>) {
isChecked.get()?.let {
isChecked.set(it.not())
}
}
@OnEvent(ClickEvent::class)
fun onCheckboxClicked(c: ComponentContext?) {
CRadio.updateCheckbox(c)
}
}
However I believe this is to be wrapped inside a parent in order to get multiple radio boxes and state has to be managed from parent. As has been mentioned in the docs:
Let’s take the example of a list of radio buttons, where you cannot have multiple items checked at the same time. The state of all the radio buttons depends on whether other items get clicked and checked. Instead of making all the radio buttons stateful, you should keep the state of what item is clicked in the parent and propagate that information top-down to the children through props.
I did quite a bit of searching but I am not able to find anything relevant. I also checked the Implementation of Spinner Component, but cant get to a good implementation. It would be a great help, if I can be guided to a concrete example?
You have to create a Radio group and manage state from there, that is the parent:
Here is one way of doing it:
@LayoutSpec
object CrgSpec {
@OnCreateLayout
fun onCreateLayout(
c: ComponentContext,
@Prop viewModel: ViewModel,
@State fields: ArrayList<RadioItem>,
@Prop parentId: String
): Component? {
val row =
Row.create(c)
.alignItems(YogaAlign.CENTER)
.alignContent(YogaAlign.SPACE_AROUND)
.flexGrow(1F)
.wrap(YogaWrap.WRAP)
fields.forEach { item ->
row.child(
CRadio.create(c)
.value(item.text)
.clickHandler(Crg.onClicked(c, item.id, item.text))
.isChecked(item.isChecked)
.id(item.id)
.build()
)
}
val column = Column.create(c)
.paddingDip(YogaEdge.TOP, 20F)
.child(row.build())
return column.child(
TextInput.create(c)
//.visibleHandler()
.backgroundRes(R.drawable.edit_text_bg)
.build()
).build()
}
@OnCreateInitialState
fun onCreateInitialState(
c: ComponentContext?,
fields: StateValue<ArrayList<RadioItem>>,
@Prop initChecked: ArrayList<RadioItem>
) {
fields.set(initChecked)
}
@OnUpdateState
fun updateCheckboxState(
fields: StateValue<ArrayList<RadioItem>>,
@Param id: String
) {
fields.get()?.let { radioItem ->
radioItem.forEach {
it.isChecked = it.id == id
}
fields.set(radioItem)
}
}
@OnUpdateState
fun updateCheckbox(
fields: StateValue<ArrayList<RadioItem>>,
@Param id: String
) {
fields.get()?.let { radioItem ->
radioItem.forEach {
it.isChecked = it.id == id
}
fields.set(radioItem)
}
}
@OnEvent(ClickEvent::class)
fun onClicked(
c: ComponentContext?,
@Prop viewModel: ViewModel,
@Param id: String,
@Prop parentId: String,
@Param itemValue: String
) {
Timber.d("id is: $id")
CirclesRadioGroup.updateCheckbox(c, id)
viewModel.onRadioUpdate(
id,
parentId,
itemValue
)
}
}
Refrences:
Bonus:- This is also a very nice example of how you would get user action callbacks in your view model and a compact example of a server driven UI on Android.