Your First Composable
With the module configured in the previous lesson, it's time to write actual Compose code. A "composable" sounds like a special kind of object, but it's really just an ordinary Kotlin function with one extra annotation. This lesson writes the smallest possible one, then wires it all the way through to a running screen.
The @Composable Annotation
@Composable marks a function as one that emits UI rather than returning a value. It
changes nothing about how you call the function syntactically — no parentheses tricks, no special
return type — but it tells the Compose compiler plugin to instrument the function so it can be
tracked, re-invoked, and skipped intelligently during recomposition (the subject of a later lesson).
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
A few rules fall directly out of what this annotation does. A @Composable function can
only be called from inside another @Composable function (or a designated entry point
like setContent { }) — the compiler enforces this, because the special tracking
machinery only exists inside that call context. A composable function also conventionally
returns Unit (nothing) — instead of returning a UI object, it emits
UI as a side effect of running, by calling other composables like Text, Column,
and Button.
A Composable Is Just a Function That Emits UI
It helps to think of every composable — even built-in ones like Text — as doing one
thing: describing a piece of UI by calling other, smaller composables, all the way down to a handful
of true primitives that Compose's rendering layer understands directly. Your own composables are no
different in kind from Text or Column — they're just user-defined functions
in that same chain:
@Composable
fun RecipeBoxApp() {
// RecipeBoxApp emits UI by calling other composables
Greeting(name = "RecipeBox")
}
This composition chain — one composable calling others, which call others in turn — is called the composition. Compose walks this chain, and every composable's job is simply to describe its own small piece of it.
Wiring It Up: setContent { RecipeBoxApp() }
The previous lesson introduced setContent { } as the bridge between an
Activity and the Compose world. Putting the pieces together, here's the complete,
minimal "Hello RecipeBox" app — a real MainActivity hosting a real composable, nothing
skipped:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
RecipeBoxApp()
}
}
}
@Composable
fun RecipeBoxApp() {
Greeting(name = "RecipeBox")
}
@Composable
fun Greeting(name: String) {
Text(text = "Hello, $name!")
}
Reading this top to bottom: when MainActivity starts, onCreate calls
setContent { }, which invokes RecipeBoxApp(). That composable in turn calls
Greeting("RecipeBox"), which calls the built-in Text composable with the
string "Hello, RecipeBox!". No XML layout file exists anywhere in this chain — the
entire screen is described in Kotlin, top to bottom, in one readable call stack.
Greeting rendered directly inside Android Studio without even running the app.RecipeBoxApp() will grow into the real root of the app — eventually deciding whether to show the Login screen or the Recipe List, and hosting navigation between Recipe List, Add/Edit Recipe, and Search. For now it just says hello, which is exactly the point: get one composable rendering correctly before adding real screens on top.