UI Components¶
The Starter Template ships a bunch of ready-made Compose Multiplatform components inside the starter/ui/components module.
They cover common screens you'll build over and over — buttons, text fields, dialogs headers, cards, chips, animations, and iOS-style (Cupertino) widgets. Everything is theme-aware, so they pick up your MaterialTheme colors automatically.
Add the dependency in your commonMain source set:
Depends on UI Utils
components implements ui-utils, so you get all the ui-utils helpers too (spacers, Dimens, popups, etc.) without extra wiring.
Buttons¶
KmpButton¶
A full-width, uniform-height button. Uses Dimens.buttonHeight (50dp) so every primary action in your app looks consistent.
LoadingButton¶
A button that swaps its content for a spinner while a task is running. It disables itself while loading so the user can't double-tap.
LoadingButton(
enabled = true,
text = "Save",
loadingText = "Saving…",
isLoading = saving,
onClick = { viewModel.onAction(FormAction.Save) },
)
GenerateAiButton¶
A "Google Blue" button with a sparkle icon that animates into a spinner while generating. Pass onDisabledClick to react when it's tapped while disabled.
GenerateAiButton(
isGenerating = generating,
text = "Generate",
generatingText = "Generating…",
onClick = { /* start generation */ },
)
GoogleSignInButtonUI¶
A bordered "Continue with Google" button that animates its width down to a circular spinner while loading.
GoogleSignInButtonUI(
isLoading = signingIn,
icon = googleLogo, // Painter
onGoogleSignInClick = { /* trigger sign in */ },
)
TimerCloseButton¶
Shows a circular countdown that turns into a close button when the timer finishes. Handy for "dismiss in X seconds" banners or ads.
Material Cupertino (iOS style)¶
PillActionButton¶
A pill-shaped FilledTonalButton with an icon on the left and text on the right.
PillActionsContainer¶
A rounded pill Surface that groups several actions in a row with spacing.
PillActionsContainer {
PillActionButton(text = "Like", icon = Icons.Default.ThumbUp, onClick = {})
PillActionButton(text = "Save", icon = Icons.Default.Bookmark, onClick = {})
}
CupertinoSwitch¶
An iOS-inspired toggle switch using Material 3 colors. Two overloads: the full one lets you tweak size/shape/colors, the short one gives sensible iOS defaults.
var isOn by remember { mutableStateOf(false) }
CupertinoSwitch(
checked = isOn,
onCheckedChange = { isOn = it },
)
Progress¶
StepsProgress¶
An onboarding-style progress bar: circular step indicators joined by animated progress lines. currentStep is 1-based.
Completed steps show a check icon; the current step is highlighted with the primary color.
Sections & Rows¶
CupertinoSection¶
An iOS "grouped list" section: an uppercase header, a rounded surface container, and an optional description below.
CupertinoSection(
title = "Account",
description = "Manage your profile settings",
) {
CupertinoSectionRow(/* ... */)
CupertinoSectionRow(/* ... */)
}
CupertinoSectionRow¶
One row inside a section. It comes in a few overloads depending on what you need on the right side:
Plain value
CupertinoSectionRow(
label = "Username",
value = "@atrii",
icon = Icons.Default.Person,
isLast = true,
)
Switch
CupertinoSectionRow(
label = "Dark mode",
icon = Icons.Default.DarkMode,
isSwitchChecked = darkMode,
onSwitchChange = { darkMode = it },
)
Stepper
CupertinoSectionRow(
label = "Quantity",
icon = Icons.Default.AddShoppingCart,
value = quantity,
onIncrement = { quantity++ },
onDecrement = { quantity-- },
)
Dropdown (works together with CupertinoDropdownMenu)
var expanded by remember { mutableStateOf(false) }
CupertinoSectionRow(
label = "Language",
value = selectedLanguage,
icon = Icons.Default.Language,
isExpanded = expanded,
onExpandedChange = { expanded = it },
) {
CupertinoDropdownItem(text = "English", onClick = { expanded = false })
CupertinoDropdownItem(text = "Spanish", onClick = { expanded = false })
}
Dropdowns¶
CupertinoDropdownMenu¶
An iOS-style dropdown popup with spring scale/fade animations. Must be used inside an ExposedDropdownMenuBox scope.
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it },
) {
// anchor
CupertinoDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
CupertinoDropdownItem(text = "Option A", onClick = { expanded = false })
CupertinoDropdownItem(text = "Option B", onClick = { expanded = false })
}
}
CupertinoDropdownItem¶
A single item. Mark it isSelected to show a checkmark on the right, and showDivider to draw a divider below.
CupertinoDropdownItem(
text = "Dark",
isSelected = true,
leadingIcon = Icons.Default.DarkMode,
showDivider = true,
onClick = { /* select */ },
)
Text¶
HeaderText¶
A centered screen header with a big primary title and a subtitle.
SheetDialogHeader¶
A header row with a title (plus optional description) and a circular close button. Ideal for dialogs and bottom sheets.
SheetDialogHeader(
label = "Filter",
description = "Narrow down results",
onCloseClicked = { /* dismiss */ },
)
ErrorText¶
A small, error-colored label for form or field errors.
FiltrationLabel¶
A subtle label for grouping/filtering sections.
DividerWithText¶
Text centered between two horizontal dividers — the classic "or continue with" separator.
TextWithButton¶
A line of text with an inline text button (like "Don't have an account? Sign up").
TextWithButton(
text = "Don't have an account?",
buttonLabel = "Sign up",
onClick = { /* navigate */ },
)
Cards¶
SelectableListCard¶
A selectable card with an animated check indicator. The container and border change when selected.
SelectableListCard(
isSelected = selected,
onClick = { selected = !selected },
) {
Text("Option one")
// more content
}
Forms¶
CleanForm¶
A titled form section container. Use it to group related fields with consistent spacing.
Images¶
CoilImage¶
An async image loader backed by Coil 3, with SVG support, caching, custom headers, and a loading placeholder/progress.
CoilImage(
url = "https://example.com/avatar.jpg",
placeHolder = Res.drawable.placeholder,
contentScale = ContentScale.Crop,
)
// SVG
CoilImage(
url = "https://example.com/logo.svg",
imageType = CoilImageType.SVG,
)
Base64Image¶
Renders an image from a Base64 string. It strips the data:image/png;base64, prefix automatically and shows a placeholder if decoding fails.
Base64Image(
base64String = "iVBORw0KGgoAAAANSUhEUg...",
contentDescription = "Profile picture",
) {
// placeholder on failure
}
Text Fields¶
FormTextField¶
A full-width outlined field with built-in error display and IME-action handling. It works with a FieldState (from starter/utils) or with plain value + error strings.
FormTextField(
value = nameState, // FieldState
onValueChange = { viewModel.onAction(FormAction.NameChanged(it)) },
label = "Full name",
imeAction = ImeAction.Next,
onImeAction = { viewModel.onAction(FormAction.Next) },
)
safeImeAction
By default safeImeAction = true, so the IME action won't fire while the field is empty or has an error. Pass clearFocusOnImeAction = true to dismiss the keyboard on Done.
FormPasswordTextField¶
Same as FormTextField but with a lock icon, a show/hide visibility toggle, and password masking.
FormPasswordTextField(
value = passwordState,
onValueChange = { viewModel.onAction(FormAction.PasswordChanged(it)) },
label = "Password",
imeAction = ImeAction.Go,
onImeAction = { viewModel.onAction(FormAction.Submit) },
)
SearchTextField¶
A search field with a search icon, a clear button, and a Search IME action.
SearchTextField(
value = query,
onValueChange = { query = it },
label = "Search",
placeholder = "Type to search…",
onSearch = { /* perform search */ },
)
Lists¶
CupertinoLazyColumn¶
A LazyColumn with iOS-style overscroll (on Android), a clipping shape, and automatic focus clearing while scrolling.
CupertinoLazyColumn(
contentPadding = PaddingValues(16.dp),
) {
items(data) { item ->
// item UI
}
}
ScrollableColumn¶
A simpler scrollable column for when you don't need a full lazy list.
Animations¶
FadeIn¶
Fades its content in, optionally after a delay. FadeInTokens provides pre-set delay constants for staggering.
Floating¶
A gentle figure-8 floating animation with optional rotation and scale "breathing". Great for hero icons or decorative elements.
Top Bars¶
SimpleNavigationTopBar¶
A TopAppBar with a title, navigation icon, and action slots.
SimpleNavigationTopBar(
title = "Settings",
onNavigationClick = { navigator.navigateUp() },
actions = {
IconButton(onClick = {}) { Icon(Icons.Default.MoreVert, null) }
},
)
SimpleMediumNavigationTopBar¶
The medium variant for a taller, more prominent header.
Chips¶
FiltrationChip¶
A FilterChip with animated leading/trailing icons. When selected, it shows a remove ("X") button by default.
FiltrationChip(
label = "Recent",
selected = selected,
leadingIcon = Icons.Default.Schedule,
onRemoveFilter = { /* clear filter */ },
onClick = { selected = !selected },
)
Other¶
LockOverlay¶
A translucent overlay with a lock icon, used to cover locked/premium content.
Support My Project ☕️¶
If you find this project useful, consider supporting it by buying me a coffee. Your support will help me to continue working on this project and add more features.