scaffolds-kmp
0.0.1indexedScroll-aware collapsing headers, parallax effects, animated collapsed titles, smooth background colour transitions, draggable tabbed scaffolds and a low-level collapsing layout for effortless polished scroll interactions.
Scroll-aware collapsing headers, parallax effects, animated collapsed titles, smooth background colour transitions, draggable tabbed scaffolds and a low-level collapsing layout for effortless polished scroll interactions.
Let’s be honest: writing custom scroll-aware animations is a pain. That’s I've we built scaffolds ( yes, with an s). It’s a KMP library designed to give your apps those high-end, polished interactions without the boilerplate.
Why you'll love it:
Scroll-Aware Magic: Effortless collapsing headers and parallax effects.
Truly Multiplatform: Native feel on iOS & Android, powerhouse performance on Desktop, and cutting-edge WASM support.
Mix & Match: Flexible components that fit your use case, not the other way around.
Stop reinventing the wheel and start building interactions that delight. 🎉
SimpleScaffold (with CollapsingLayout) | CollapsibleScaffold |
|---|---|
![]() | ![]() |
Add the dependency to your build.gradle.kts:
implementation("com.dontsaybojio:scaffolds:0.0.1")
Make sure mavenCentral() is in your repository list:
repositories {
mavenCentral()
}
The library ships three components that build on top of each other. Pick the one that fits your needs!
SimpleScaffoldA convenient wrapper that combines CollapsingLayout with a Material3 Scaffold. You bring your
own TopAppBar and expanded header — SimpleScaffold handles all the padding and scroll wiring for
you.
Parameters
CollapsibleScaffoldThe fully opinionated, batteries-included scaffold. It gives you:
Parameters
CollapsingLayoutThe low-level building block. It stacks a collapsible header on top of your body content and wires up the nested scroll so the header slides away as the user scrolls down — and comes back when they scroll up.
You can drive it with Material3's TopAppBarScrollBehavior (for the classic exit-until-collapsed
look) or let it manage the scroll state internally.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyScreen() {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
CollapsingLayout(
scrollBehavior = scrollBehavior,
collapsingTop = {
// Your collapsible header goes here
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.background(MaterialTheme.colorScheme.primary)
)
},
bodyContent = {
LazyColumn {
items() { index ->
ListItem(headlineContent = { Text() })
}
}
}
)
}
Parameters
| Tool | Version |
|---|---|
| Kotlin | 2.x |
| Compose Multiplatform | 1.x |
| Material3 |
CollapsingLayout inspired by Ayushi GuptaCollapsibleScaffold inspired
by Dani Mahardhika
fun MyScreen(navController: NavHostController) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberTopAppBarState()
)
SimpleScaffold(
scrollBehavior = scrollBehavior,
topBar = {
TopAppBar(
title = { Text("My Screen") },
navigationIcon = { /* back button */ },
actions = { /* action icons */ }
)
},
expandedHeader = {
// The section that collapses as the user scrolls
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.primary)
.padding(16.dp)
) {
Text(
text = "Aerith Gainsborough",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onPrimary
)
Text(
text = "A flower peddler living in the Sector 5 slums.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onPrimary
)
}
},
content = {
LazyColumn {
items(100) { index ->
ListItem(headlineContent = { Text("Item $index") })
}
}
}
)
}
| Parameter | Description |
|---|
scrollBehavior | The TopAppBarScrollBehavior that drives the collapse. |
topBar | Your TopAppBar composable. |
expandedHeader | The header content that collapses on scroll. |
bottomBar | Optional bottom bar composable. |
content | The body content. |
fun MyScreen(navController: NavHostController) {
val pagerState = rememberPagerState(pageCount = { tabs.size })
val scope = rememberCoroutineScope()
CollapsibleScaffold(
expandedHeaderBackground = MaterialTheme.colorScheme.tertiary,
collapsedHeaderBackground = MaterialTheme.colorScheme.secondary,
contentBackgroundColor = MaterialTheme.colorScheme.surface,
navigationIcon = {
IconButton(onClick = { navController.navigateUp() }) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
}
},
actionIcon = {
IconButton(onClick = { }) {
Icon(Icons.Outlined.Info, contentDescription = "Info")
}
},
expandedHeader = { modifier ->
// The big header with parallax & fade applied via the provided modifier
Column(
modifier = modifier.padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = "Aerith Gainsborough",
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onTertiary,
fontWeight = FontWeight.Bold
)
Text(
text = "A flower peddler living in the Sector 5 slums.",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onTertiary
)
}
},
collapsedHeader = {
// Shown in the TopAppBar title area once the header has collapsed
Text(
text = "Aerith Gainsborough",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
},
tabView = {
// Tab row (or any sticky header) shown above the scrollable content
PrimaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = Color.Transparent,
edgePadding = 0.dp
) {
tabs.forEachIndexed { index, tab ->
Tab(
selected = pagerState.currentPage == index,
text = { Text(tab.name) },
onClick = { scope.launch { pagerState.animateScrollToPage(index) } }
)
}
}
},
content = { scrollBehavior ->
// Pass the scrollBehavior down to your pager / list
HorizontalPager(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
state = pagerState
) { page ->
LazyColumn {
items(100) { index ->
ListItem(headlineContent = { Text("Item $index") })
}
}
}
}
)
}
| Parameter | Description |
|---|
expandedHeaderBackground | Background colour when the header is fully expanded. |
collapsedHeaderBackground | Background colour when the header is fully collapsed. The two colours smoothly lerp between each other. |
contentBackgroundColor | Background colour of the rounded content card. |
cornerRadius | Corner radius of the content card. Defaults to 16.dp. |
parallaxFactor | How much the expanded header translates upward relative to the scroll. Defaults to 0.3f. |
navigationIcon | Optional navigation icon (e.g. back button). |
actionIcon | Optional action icon(s) in the TopAppBar. |
expandedHeader | The large header content. Receives a Modifier that carries the parallax translation and fade — apply it to your root composable. |
collapsedHeader | Content shown in the TopAppBar title area after the header has collapsed. |
tabView | Sticky tab row (or any header) shown between the top bar and the scrollable content. |
content | The scrollable body. Receives a TopAppBarScrollBehavior to pass to your list/pager via Modifier.nestedScroll. |
bottomBar | Optional bottom bar composable. |
| Parameter | Description |
|---|
collapsingTop | The collapsible header content. |
bodyContent | The body content displayed below the header. |
modifier | Modifier applied to the outer Box. |
scrollBehavior | Optional TopAppBarScrollBehavior. When provided, its nestedScrollConnection drives the collapse. Defaults to an internal connection when null. |
| 1.x |
| Android min SDK | 23 |
Surfaced from shared tags and platforms — no rankings paid for.