What You Will Build

A colorful note-taking application featuring a two-column grid layout, floating action button for adding notes, and Material 3 card styling. Each note has its own accent color and displays a title with truncated content preview. This pattern is the foundation for productivity apps like Google Keep.

Why This Pattern Matters

Grid-based note layouts are everywhere in modern productivity apps. Building one teaches you LazyVerticalGrid for responsive column layouts, mutableStateOf with list management, and Card composables with custom colors. These are core skills for any data-driven Compose UI.

Key Compose Concepts

  • LazyVerticalGrid with GridCells.Fixed(2) for the two-column masonry layout.
  • mutableStateOf with data classes for reactive note list management.
  • Scaffold with FloatingActionButton for the standard Material 3 app shell.
  • CardDefaults.cardColors for per-card tinted backgrounds.

Step 1: Define the Note Model

Each note has a unique ID, title, content, and an accent color:

data class Note(
    val id: String = java.util.UUID.randomUUID().toString(),
    val title: String,
    val content: String,
    val color: Color
)

Step 2: Set Up the State

Initialize a list of sample notes. The state holder uses mutableStateOf so adding or removing notes triggers recomposition:

var notes by remember {
    mutableStateOf(listOf(
        Note(title = "Shopping List", content = "Milk, Bread, Eggs", color = Color(0xFFFFEB3B)),
        Note(title = "Meeting Notes", content = "Discuss Q2 goals", color = Color(0xFF81D4FA)),
        Note(title = "Ideas", content = "New app concept", color = Color(0xFFA5D6A7)),
        Note(title = "Workout", content = "Chest and triceps", color = Color(0xFFFFAB91))
    ))
}

Step 3: Build the Grid Layout

Use Scaffold for the top bar and FAB, then LazyVerticalGrid for the two-column layout. Each card gets its note color at reduced opacity:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NoteAppScreen() {
    Scaffold(
        topBar = { TopAppBar(title = { Text("SD Note App") }) },
        floatingActionButton = {
            FloatingActionButton(onClick = {
                notes = notes + Note(
                    title = "New Note",
                    content = "Tap to edit",
                    color = Color(0xFFCE93D8)
                )
            }) {
                Icon(Icons.Default.Add, "Add")
            }
        }
    ) { padding ->
        LazyVerticalGrid(
            columns = GridCells.Fixed(2),
            modifier = Modifier.padding(padding).padding(8.dp),
            verticalArrangement = Arrangement.spacedBy(8.dp),
            horizontalArrangement = Arrangement.spacedBy(8.dp)
        ) {
            items(notes, key = { it.id }) { note ->
                Card(
                    Modifier.fillMaxWidth().height(150.dp),
                    colors = CardDefaults.cardColors(
                        containerColor = note.color.copy(alpha = 0.3f)
                    )
                ) {
                    Column(Modifier.padding(12.dp)) {
                        Text(note.title, fontWeight = FontWeight.Bold)
                        Spacer(Modifier.height(4.dp))
                        Text(
                            note.content,
                            maxLines = 4,
                            overflow = TextOverflow.Ellipsis,
                            color = MaterialTheme.colorScheme.onSurface
                                .copy(alpha = 0.7f)
                        )
                    }
                }
            }
        }
    }
}

Tips and Pitfalls

  • Always provide a key to items(). Without stable keys, the grid cannot animate item additions or removals correctly.
  • Use copy(alpha = 0.3f) to tint card backgrounds without overwhelming the text content.
  • TextOverflow.Ellipsis with maxLines prevents long notes from breaking the fixed card height.
  • GridCells.Adaptive(minSize = 160.dp) is a good alternative if you want the grid to adapt to screen width automatically.

Min SDK: 21 | Compose BOM: 2024.01.00+

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.