Reputation: 23824
I wrote a simple wrapper for the Text
composable to use a string resource.
@Composable
fun Text(@StringRes id: Int) = Text (stringResource(id))
Next I did the same for bold text.
val bold = TextStyle(fontWeight = FontWeight(600))
@Composable
fun Bold (text: String) = Text (style = bold, text = text)
@Composable
fun Bold (@StringRes id: Int) = Bold (stringResource(id))
Now I realized that the two composables using the string resource look quite the same. So I tried to make them generic. But this does not work:
@Composable
fun <C: Composable>WithStrRes (@StringRes id: Int) = C(stringResource(id))
The error is:
Type parameter C cannot be called as function
How to fix this?
Using function references does not work either. When I try to replace the redundant definitions with a builder
// @Composable
// fun Text(@StringRes id: Int) = Text (stringResource(id))
// @Composable
// fun Bold (@StringRes id: Int) = Bold (stringResource(id))
fun withStrRes(composable: (String) -> Unit): (Int) -> Unit =
@Composable
fun (@StringRes id: Int) {
composable(stringResource(id))
}
val Text: (id: Int) -> Unit = withStrRes(::Text)
val Bold: (id: Int) -> Unit = withStrRes(::Bold)
I get the error
Function References of @Composable functions are not currently supported
Upvotes: 3
Views: 1812
Reputation: 3429
The thing you are trying to achieve is not possible in kotlin. You are trying to give type parameter C
extending Composable
.
Since compose is based on kotlin functions (Not the classes) , inheritance is the not possible for functions.
Also function cannot be passed as type parameter in kotlin. Only classes can be added as type parameters.
Here the Composable
is an annotation class
. If you define like below only Composable class can be passed as type. Annotation classes are final and it is not possible to create another class extending Composable class.
@Composable
fun <C: Composable>WithStrRes (@StringRes id: Int)
So your definition only allows to call function like below which is unwanted in compose.
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
WithStrRes<Composable>(id = R.string.app_name)
//Here type param should be Composable class itself. This is not required
}
The way to achieve reusable composables is by following way definining higher order functions of @Composable
type as reuse below.
Here second parameter accepts only functions which is annotated with @Composable
Generic Composable:
@Composable
fun Bold(text: String) = Text(style = bold, text = text)
@Composable
fun Bold(@StringRes id: Int) = Bold(stringResource(id))
@Composable
fun WithStrRes(@StringRes id: Int, C: @Composable (id: Int) -> Unit) {
C(id)
}
Usage:
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
StackOverflowAndroidTheme {
Greeting("Android")
}
WithStrRes(id = R.string.app_name) {
Bold(id = it)
}
}
Upvotes: 1