I am using a composable view that show a view for different types of objects.
@Composable
fun Details(
liveData: LiveData<State>,
onAction: ((Action) -> Unit)? = null
) {
val state = liveData.observeAsState()
when (state.value) {
is AuthorState -> {
AuthorDetails(state, onAction)
}
is BookState -> {
BookDetails(state, onAction)
} else -> {
DefaultState(state, onAction)
}
}
}
sealed class Action<T> {
class Name<T>(val id: T): Action<T>()
}
sealed class AuthorAction {
class Name(val id: Long): Action<Long>()
class Address(val author: AuthorState): AuthorAction()
}
@Composable
fun AuthorDetails(
authorState: AuthorState,
onAction: (AuthorAction) -> Unit
) {
...
onAction(AuthorAction.Address(authorState))
...
}
sealed class BookAction {
class Name(val id: String): Action<String>()
class Location(val book: BookState): BookAction()
}
@Composable
fun BookDetails(
bookState: BookState,
onAction: (BookAction) -> Unit
) {
...
onAction(BookAction.Location(bookState))
...
}
And in Activity:
view.bottom_sheet.setContent {
Details(viewModel.details) { action ->
when(action) {
is BookAction -> onBookAction(action)
is AuthorAction -> onAuthorAction(action)
is DefaultAction -> onDefaultAction(action)
}
}
}
I am trying to define a hierarchy of actions that are based on the state that is passed to the composable.
Problem is the "Details" composable expects an "Action" type, not BookAction or AuthorAction. I have played around with the inheritance, however usually end up with a lot of duplication.
Edit: Using @broot answer, having issues invoking action from composable:
Adding back in "id" template:
sealed interface Action<I>
sealed interface AuthorAction<I> : Action<I>
sealed interface BookAction<I> : Action<I>
class Name<I>(val id: I) : Action<I>, AuthorAction<I>, BookAction<I>
class Address(val author: AuthorState) : AuthorAction<Long>
class Location(val book: BookState) : BookAction<String>
interface State<I, out A : Action>
interface BookState : State<String, BookAction>
interface AuthorState : State<Long, AuthorAction>
interface DefaultState<I> : State<I, Action>
fun <I, A : Action<I> Details(
liveData: LiveData<State<I, A>>,
onAction: ((A) -> Unit)? = null
) {
onAction?.invoke(Name("id"))
}
Doing this gives me a compile error:
Type mismatch.
Required: A
Found: Action<I>
I can change the "Details" function:
fun <I, A : Action> Details(
liveData: LiveData<State<I, A>>,
onAction: ((A: Action<I>) -> Unit)? = null
) {
onAction?.invoke(Name("id"))
}
However when I do this, action is always "Action" in client calling code, not BookAction or AuthorAction.