Reputation: 1861
I'm having a hard time understanding Composable functions.
The example provided by Google looks like this:
@Composable
fun MessageCard(name: String) {
Text(text = "Hello $name!")
}
This is a function that returns no value. The first line of the function instantiates an object of type Text, but assigns it to nothing. I can't see how this can have any effect at all, yet somehow it creates a Text area that ends up on the screen. How?
The example also shows a function being declared outside of a class, which is not something I thought you could do. At least, you can't do it in Java and although I am no expert in Kotlin, I always assumed that it ran on the same JVM and followed the same rules.
Frank
Upvotes: 1
Views: 1210
Reputation: 67179
@Composable fun MessageCard(name: String {
Text(text = "Hello $name!")
}
For every default ui Composable like the one above Modifiers and Layout
composable are used in implementation.
Layout Composable is basis of every ui composable which is
@Composable
@UiComposable
inline fun Layout(
modifier: Modifier = Modifier,
measurePolicy: MeasurePolicy
) {
val compositeKeyHash = currentCompositeKeyHash
val materialized = currentComposer.materialize(modifier)
val localMap = currentComposer.currentCompositionLocalMap
ReusableComposeNode<ComposeUiNode, Applier<Any>>(
factory = ComposeUiNode.Constructor,
update = {
set(measurePolicy, SetMeasurePolicy)
set(localMap, SetResolvedCompositionLocals)
set(materialized, SetModifier)
@OptIn(ExperimentalComposeUiApi::class)
set(compositeKeyHash, SetCompositeKeyHash)
},
)
}
It creates a node which by adding containing other children or by being inside other Composables creates a ui node tree.
https://developer.android.com/jetpack/compose/phases
Compose has three main phases:
In first phase a tree is created to show where each ui @Composable should be in this tree. Contents of a composable or other one is set such as for instance for some Composable
CustomLayout() {
println("Parent Scope")
CustomLayout() {
println("Child1 Outer Scope")
Text("Child1 Outer Content $text")
CustomLayout() {
println("Child1 Inner Scope")
Text("Child1 Inner Content $text")
}
}
CustomLayout() {
println("Child2 Scope")
Text("Child2 Content $text")
}
}
Parent Layout
/ \
/ \
/ \
/ \
Child1 Outer Child2
|
Child1 Inner
In second phase with Layout these Layout
functions measure their content based on their Modifiers or Constraints, then get total dimensions of its children and also based on its Modifier or Constraints it sets its dimensions. Then places children according to to logic in MeasurePolicy
.
https://developer.android.com/jetpack/compose/layouts/custom
Laying out each node in the UI tree is a three step process. Each node must:
Measure any children
Decide its own size
Place its children
After measurement and placement based on how their draw modifier dictates them to draw their content they draw the Composable.
A draw modifier is basically something like this
private class DrawWithContentModifier(
var onDraw: ContentDrawScope.() -> Unit
) : Modifier.Node(), DrawModifierNode {
override fun ContentDrawScope.draw() {
onDraw()
}
}
And the reason @Composable
functions return Unit is to create scope to limit recomposition bounds.
https://dev.to/zachklipp/scoped-recomposition-jetpack-compose-what-happens-when-state-changes-l78
For every non-inline composable function that returns Unit, the Compose compiler generates code that wraps the function’s body in a recompose scope.
Recompose scopes are an important piece of the Compose puzzle. They do some bookkeeping and help reduce the amount of work Compose has to do to prepare a frame.
They are the smallest unit of a composition that can be re-executed (recomposed) to update the underlying tree. They keep track of what snapshot-based State objects are read inside of them, and get invalidated when those states change.
Upvotes: 3
Reputation: 7248
Yes, in Kotlin, you can declare a function outside of class - it is called a top level function. You can't do that in java, so what Kotlin does is they create that class for you and make that top level function a static function of that artificial class. So let's say you have a file Utils.kt
:
fun foo(): Int = 1
it is equivalent to this java code:
public class UtilsKt {
public static int foo() {
return 1;
}
}
and UtilsKt.foo()
is also how you can call your Kotlin function from Java. You can read more about this in this blogpost.
The answer to how function that seemingly does nothing can actually render UI to the screen is a compiler plugin. Jetpack compose has a Kotlin compiler plugin that takes all Kotlin functions annotated with @Composable
and adds code for compose runtime that does the magic. You can read more about it and see how the transformed functions look like here.
Upvotes: 0