A versatile and modern UI component library for Jetpack Compose, offering a collection of adaptive layouts, animated navigation bars, custom pickers, and more.
jetpack-ui is a curated suite of powerful and reusable composables designed to accelerate UI
development. It includes solutions for common patterns like adaptive two-pane layouts, animated
navigation, overflow menus, and unique text pickers. Each component is self-contained, easy to
integrate, and built with customization in mind.
-
TwoPaneAdaptiveLayout: A layout that automatically adapts to screen size, showing a two pane in Column for smaller screens otherwise show in Row.
-
AnimatedBottomNavBar: A visually engaging bottom navigation bar with smooth animations for item selection, including icon scaling and label transitions.
-
BottomOptionBar: An adaptive bottom bar that displays as many primary actions as can fit on one line and elegantly tucks the rest into a "More" overflow menu.
-
DialTextPicker: A unique, circular text picker that allows users to select an item by rotating a dial, perfect for time or numerical input.
-
WheelTextPicker: A classic "slot-machine" style wheel picker for selecting an item from a vertical list, with haptic feedback and snapping behavior.
-
BasicTextEditor: A simple, ready-to-use text field composable styled with
OutlinedTextFieldand designed for easy integration.
Groovy (build.gradle):
dependencies {
implementation 'com.github.bashpsk.emptylibs:jetpack-ui:<latest-version>'
}Kotlin DSL (build.gradle):
dependencies {
implementation("com.github.bashpsk.emptylibs:jetpack-ui:<latest-version>")
}Kotlin DSL with Version Catalogs:
[versions]
empty-libs = "<latest-version>"
[libraries]
emptylibs-jetpack-ui = { group = "com.github.bashpsk.emptylibs", name = "jetpack-ui", version.ref = "empty-libs" }dependencies {
implementation(libs.emptylibs.jetpack.ui)
}An adaptive layout for list-detail views. It takes two composable slots: firstPane and
secondPane.
TwoPaneAdaptiveLayout(
firstPane = { Image() },
secondPane = { ImageInfo() },
aspectRatio = imageRatio
)A bottom navigation bar with smooth animations.
Scaffold(
bottomBar = {
AnimatedBottomNavBar {
navItems.forEach { item ->
BottomNavItem(
isSelected = item == selectedItem,
label = item.label,
icon = item.icon,
onItemClick = { selectedItem = item }
)
}
}
}
) { paddingValues ->
// Screen content
}An adaptive bottom bar that displays as many primary actions as can fit on one line and elegantly tucks the rest into a "More" overflow menu.
val options = listOf(
OptionBarData(label = "Edit", icon = Icons.Default.Edit),
OptionBarData(label = "Favorite", icon = Icons.Default.Favorite),
OptionBarData(label = "Share", icon = Icons.Default.Share),
OptionBarData(label = "Delete", icon = Icons.Default.Delete),
OptionBarData(label = "Info", icon = Icons.Default.Info)
).toImmutableList()
// This will show as many items as can fit on one line,
// and the rest will be in a "More" menu.
BottomOptionBar(
optionList = options,
onOptionClick = { option ->
// Handle option click
},
maxLines = 1
)A circular picker for selecting from a list of strings.
val hours = (0..23).map { it.toString().padStart(2, '0') }.toImmutableList()
val dialState = rememberDialTextPickerState(textList = hours, initial = "08")
DialTextPicker(state = dialState)
// Observe selected item
Text("Selected Hour: ${dialState.selectedText}")A vertical wheel-style picker.
val minutes = (0..59).map { it.toString() }.toImmutableList()
val wheelState = rememberWheelTextPickerState(textList = minutes)
WheelTextPicker(
state = wheelState,
visibleCount = 5, // Show 5 items at a time
textStyle = MaterialTheme.typography.bodyMedium,
dividerFraction = 0.5F, // 50% of width from 'WheelTextPicker' width
dividerColor = MaterialTheme.colorScheme.primary,
dividerThickness = 4.dp
)
// Observe selected item
Text("Selected Minute: ${wheelState.selectedText}")A simple, line-numbered BasicTextField wrapper.
var code by remember { mutableStateOf("fun main() {\n println(\"Hello, World!\")\n}") }
BasicTextEditor(
modifier = Modifier.fillMaxSize(),
inputContent = code,
onContentChange = { newContent ->
code = newContent
}
)| Adaptive Bottom Bar | Adaptive Bottom Bar - Overflow Menu | Adaptive Bottom Bar - Landscape |
|---|---|---|
![]() |
![]() |
![]() |
bottom_option_bar.mp4
| Dial Text Picker | Wheel Text Picker |
|---|---|
![]() |
![]() |
dial_text_picker.mp4
wheel_text_picker.mp4
| Basic Text Editor |
|---|
![]() |





