Search code examples
androidkotlinfunctional-programmingandroid-jetpack-composefunctional-interface

How to write Composable function using Kotlin Functional (SAM) Interface


We can write functional interfaces in Kotlin like this - function-interfaces

fun interface Sum {
    fun add(a: Int, b: Int): Int
}

val sumImpl = Sum { a, b ->
    return@Sum a + b
}

val testSum = sumImpl.add(4, 5)

How can we write Jetpack Composable function in same way? Below code is not working. `

fun interface SampleText {
    @Composable
    fun text(data : String)
}

val textImpl = SampleText { data ->
    return@SampleText @Composable { Text(data) }
}

@Composable
fun testText() = textImpl.text("Data")

I have tried this as well, but this also didn't work.

fun interface SampleText {
    fun text(data : String) : @Composable () -> Unit
}

val textImpl = SampleText { data ->
    @Composable { Text(data) }
}

@Composable
fun testText() = textImpl.text("Data")

Solution

  • The first version is not compiling in its lambda form because your interface function returns a Unit and your'e actually having a Type mismatch error, its just weird the compiler reports Internal Error when you try to return a @Composable annotated function, but the issue becomes clear if you simply return something like a String.

    enter image description here vs enter image description here

    To solve your first version, either you fully declare an object of the class like this (though its useless since you want a lambda version of your SAM interface not an actual object in the first place)

    val textImpl = object: SampleText {
        
        @Composable
        override fun text(data: String) {
            Text(data)
        }
    }
    

    , but it will work just by simply calling the testText() function like this.

    testText()
    

    Or change it to your second version.


    Now for your second version, since your interface returns a @Composable lambda, you have to invoke it as well in the call-site, making two function invocations to make it work,

    testText()() // two invocations
    

    first call invokes your testText() function, second pair of parenthesis invokes the @Composable lambda from your interface.

    Or simply call .invoke()

    testText().invoke() // just call the .invoke() of the returned composable lambda
    

    Either of the implementations and calls display the text "Data"

    enter image description here