Jetpack Compose is officially stated as Android’s modern toolkit for building native UI which simplifies and accelerates UI development on Android and, quickly brings apps to life with less code, powerful tools, and intuitive Kotlin APIs.
Google has introduced its latest toolkit Jetpack Compose for building Android applications and it is expected to replace Android View System. It is not the only available declarative UI concept. React Native, Flutter, and Swift UI all are based on the declarative UI concept.
Here, in this article, I am trying to help newcomers to understand the basics of Jetpack Compose using Android Studio. The stable android studio version while writing this article is Chipmunk. Let's hop on.
First Jetpack Compose Project
The best way to create compose project is through the New Project in Android Studio as in the images below.
After a new project is created and successfully build, Hello Android
is shown in the above image in the preview panel.
Let's break down the initial project and try to understand a few basics.
Project Structure
The generated android project has the following folder structure and the application starts with MainActivity along with theme-related files like Color.kt
, Theme.kt
, Type.kt
.
Composable Functions
Composable functions must be annotated with @Composable
at the top. These functions do not have a return type but can have multiple function parameters. The content inside the composable function is rendered to make the application layout design.
@Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Preview
The design preview is generated by the Android Studio if any composable function is further annotated with @Preview
.
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
Greeting("Android")
}
}
If showSystemUI
is set to true in Preview. The design preview is shown in Android Device Frame.
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
Greeting("Android")
}
}
MainActivity.kt
This file (activity class) is the start point for working with Compose UI. This activity class must also be defined inside the app/src/main/AndroidManifest.xml
as launcher activity. Compose codes must be written inside setContent
dsl function.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
FirstComposeAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Greeting("Android")
}
}
}
}
}
Theme.kt
This file is used to define color schemes and themes for the composable functions (UI components) used inside the application. Here, a composable function is defined to provide the themed wrapper to the child composables. This wrapper helps to inherit the defined theme to the child compose UI components.
@Composable
fun FirstComposeAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
(view.context as Activity).window.statusBarColor = colorScheme.primary.toArgb()
ViewCompat.getWindowInsetsController(view)?.isAppearanceLightStatusBars = darkTheme
}
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}
Basic Composable Components
High level element that displays text and provides semantics / accessibility information.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
FirstComposeAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
LayoutBasics()
}
}
}
}
}
@Composable
fun LayoutBasics() {
Text(text = "Hello Jetpack!")
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
Here, LayoutBasics
is a composable function that contains another composable function Text
and renders the text Hello Jetpack!
as in image below.
The Text
function has various parameters to define the color, font size, font type, font style, text alignment, etc. Here is the example of a text with two lines, overflow ellipsis and other various stylings.
@Composable
fun LayoutBasics() {
val longText =
"Hello Jetpack!\nThe quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog"
Text(
text = longText,
modifier = Modifier.padding(16.dp),
color = Color.Red,
fontSize = 20.sp,
fontStyle = FontStyle.Italic,
letterSpacing = 5.sp,
textDecoration = TextDecoration.Underline,
textAlign = TextAlign.Start,
overflow = TextOverflow.Ellipsis,
softWrap = true,
maxLines = 2,
style = TextStyle.Default,
)
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
The detailed explanation on Text
and TextField
can be found here.
Image
Creates a composable that lays out and draws a given source like painter. The drawable image resource is passed inside the painter resource and is used as a parameter for Image
composable.
@Composable
fun LayoutBasics() {
// image path: app/src/main/res/drawable/jetpack_compose.png
Image(
painter = painterResource(R.drawable.jetpack_compose),
contentDescription = null,
modifier = Modifier
.padding(16.dp)
.size(100.dp)
.border(1.dp, MaterialTheme.colorScheme.secondary, RoundedCornerShape(8.dp))
)
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
Vector Images can also be rendered like in example below.
@Composable
fun LayoutBasics() {
Image(
imageVector = Icons.Filled.AccountCircle,
contentDescription = null, // decorative
modifier = Modifier.size(40.dp),
colorFilter = ColorFilter.tint(color = Color.LightGray),
contentScale = ContentScale.Fit
)
}
Row
A layout composable that places its children in a horizontal sequence.
A Row
composable used to place components in a horizontal manner side by side. Here is a example of Image icons in a row.
@Composable
fun LayoutBasics() {
Row {
ImageIcon()
ImageIcon()
ImageIcon()
}
}
@Composable
fun ImageIcon(){
Image(
imageVector = Icons.Filled.AccountCircle,
contentDescription = null, // decorative
modifier = Modifier.size(40.dp),
colorFilter = ColorFilter.tint(color = Color.Gray),
contentScale = ContentScale.Fit
)
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
Column
A layout composable that places its children in a vertical sequence.
A Column
composable to used to place components in a verticle manner one after another. Here is a example of texts in a column.
@Composable
fun LayoutBasics() {
Column {
Text("First Text")
Text("Second Text")
Text("Third Text")
Text("Fourth Text")
}
}
@Preview(showBackground = true, showSystemUi = false)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
Combination of Row and Column
Row and Column can be combined to represent UI components. Let's look at the following example. The column contains an Image and Row. The inner row contains an image and another column.
@Composable
fun LayoutBasics() {
Column(
Modifier
.background(Color.LightGray)
.padding(16.dp)
) {
Image(
painter = painterResource(R.drawable.begin_cup),
contentDescription = null,
modifier = Modifier.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Fit
)
Row(
modifier = Modifier
.background(Color.White)
.fillMaxWidth()
.padding(PaddingValues(16.dp))
) {
Image(
imageVector = Icons.Default.AccountCircle,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(Color.LightGray),
contentDescription = null,
contentScale = ContentScale.Fit,
colorFilter = ColorFilter.tint(color = Color.Green)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(text = "Begin Cup", fontWeight = FontWeight.Bold)
Text(text = "A good morning coffee")
}
}
}
}
@Preview(showBackground = true, showSystemUi = true)
@Composable
fun DefaultPreview() {
FirstComposeAppTheme {
LayoutBasics()
}
}
In this article, I covered some basics of Jetpack Compose. In the future, I will cover more advanced topics. Stay tuned!
References