It’s the Compiler!

To answer this basic question, we need to dig deep inside the workings of Composable function.

When we mark a function as @Composable, it gets some superpowers, such as it can now add a node to a Compose UI tree to explain it in simple terms. To understand, how it is a node let’s write a basic composable.

@Composable
fun IAmAComposable(){
Text("I am a Composable fun, why fun? because compose is fun!! hmm fun ok")
}

The composable we defined when goes through composition does not return anything, but if not how is the UI rendered? Is the text composable doing it? Hmm, maybe.

When a composition happens a Node is created inside the composable tree with all of the implementation inside the composable. This tree when traversed renders the UI. This is not limited only to UI but compose runtime and compiler can be used to traverse any kind of tree structure efficiently, more on that here.

But you came here to know why can’t we call a composable from a normal function. So, let’s answer that.

In Android development, various types of contexts, like fragment, activity, and application contexts, play crucial roles in managing the lifecycle and scope of resources within an application.

Each context is associated with a specific component’s lifecycle, such as a fragment or activity. By using these contexts, it can be ensured that services and resources are accessed and managed within the appropriate scope, thereby enhancing the overall efficiency and reliability of the application.

Similarly, after compilation, every composable function receives an additional parameter called the “Composer”. This parameter is injected using by the compose compiler.

@Composable
fun IAmAComposable($composer: $Composer<*>){
$composer.start(\\unique_key_here)
IAmComposablesChild($composer)
Text("I am a Composable fun, why fun? because compose is fun!! hmm fun ok", $composer)
$composer.end()
}

@Composable
fun IAmComposablesChild($composer: Composer<*>){
}

This $composer is then also passed to the children of that composable. Note that, this composer at the start receives a unique key which is then used to identify in the composable tree which node belongs to which composer. It’s mandatory and should be available for every node in the tree, considering the above composable you should be able to understand that from the parent composable to the last composable in the tree same composer instance will be used.

Just like when an activity has multiple fragments but the activity context for all of them will be the same.

That is why a Composable function can not be called from a normal function. Since every composable needs a parent context in the form of the Composer, if it is not present composition can not happen. Since what is composition? it is traversing the compose tree and rendering(if required) every node on the screen if it does not have the parent node it can’t be rendered.

This composer is then used by Compose Runtime to efficiently traverse the compose UI tree.

This is a fairly simple example and explanation but you can learn all this in detail from these references:

Happy learning! ❤

Source link