lumo-ui
repository·main·Indexed 20 days ago
https://github.com/nomanr/lumo-uiA Gradle plugin and CLI developer tool for streamlining the creation of Jetpack Compose UIs. It generates source code for customizable UI components—such as Accordions with AccordionState and AccordionGroupState, TopBar scroll behaviors (Pinned, EnterAlways, and ExitUntilCollapsed), and customizable ripple effects—which developers can copy and paste directly into their Android projects for full codebase control.
What's inside lumo-ui
- lumo-ui is a Gradle plugin that uses a CLI to generate customizable Jetpack Compose UI components. Instead of managing a heavy library dependency, it provides ready-to-use components that you can copy and paste directly into your Android projects. This approach allows for full control over the component code within your own codebase.
Explore lumo-ui components via the sample app
mainTo see the components in action and explore their visual styles, you can download the official sample application. This is useful for deciding which components fit your design requirements before generating them for your project.
Download from: - Google Play Store: https://play.google.com/store/apps/details?id=com.nomanr.lumo.sample - Direct APK: https://lumoui.com/lumo-ui.apkManage multiple accordions with AccordionGroupState
mainWhen you have a list of accordions and want to control their relationship (e.g., ensuring only one is open at a time), use
AccordionGroupState.Initialization: Use
rememberAccordionGroupState(count, allowMultipleOpen)to create the group controller.Key Features:
- Single Expansion Mode: If
allowMultipleOpenisfalse(default), expanding one accordion will automatically collapse all others in the group. - Multiple Expansion Mode: If
allowMultipleOpenistrue, multiple accordions can be open simultaneously.
Methods:
getState(index: Int): Returns theAccordionStatefor the specific accordion at that index. This state must be passed to theAccordioncomponent to link it to the group logic.expand(index: Int): Programmatically expands the accordion at the given index.collapseAll(): Closes all accordions in the group.
val groupState = rememberAccordionGroupState(count = 3, allowMultipleOpen = false) Column { repeat(3) {/n val state = groupState.getState(it) Accordion( state = state, headerContent = { Text("Accordion #$it") }, bodyContent = { Text("Content for #$it") } ) }- Single Expansion Mode: If
Configure ripple effects with ripple()
mainUse the
ripple()function to create anIndicationNodeFactoryfor visual feedback during interactions. You can customize the ripple's boundary, radius, and color.There are two main overloads:
- Static Color: Pass a fixed
Color. - Dynamic Color: Pass a
ColorProducerfor colors that can change dynamically (e.g., based on theme or state).
Parameters:
bounded: Iftrue, the ripple is constrained to the component's bounds. Defaults totrue.radius: The radius of the ripple. IfDp.Unspecified, it uses the default behavior.color: The color of the ripple. IfColor.Unspecified, it falls back toLocalContentColoror the color provided inLocalRippleConfiguration.
// Using a static color val myRipple = ripple(color = Color.Red, bounded = true) // Using a dynamic color producer val dynamicRipple = ripple(color = { Color.Blue }, radius = 20.dp)- Static Color: Pass a fixed
Customize ripple behavior via LocalRippleConfiguration
mainYou can provide a global or scoped
RippleConfigurationusingCompositionLocalProviderwithLocalRippleConfiguration. This allows you to set a default color and alpha for all ripples within a specific UI subtree.RippleConfigurationproperties:color: AColorused if the specific ripple instance usesColor.Unspecified.rippleAlpha: ARippleAlphaobject defining the opacity for different interaction states (pressed, focused, dragged, hovered). Ifnull,RippleDefaults.RippleAlphais used.
val customConfig = RippleConfiguration( color = Color.Blue, rippleAlpha = RippleAlpha( pressedAlpha = 0.2f, focusedAlpha = 0.1f, draggedAlpha = 0.15f, hoveredAlpha = 0.05f ) ) CompositionLocalProvider(LocalRippleConfiguration provides customConfig) { // All ripples inside this block will use customConfig unless overridden locally }Use the Accordion component
mainThe
Accordioncomponent is a Compose UI element that displays a header and an expandable body. It supports optional animation and can be controlled via anAccordionStateobject.Parameters:
modifier: Modifier for the outer container.headerModifier: Modifier for the header area.state: AnAccordionStateinstance (userememberAccordionState()for default behavior).animate: Boolean to enable/disable vertical expansion/shrink animations (defaults totrue).interactionSource:MutableInteractionSourcefor handling interactions like ripples.headerContent: A composable lambda defining the header UI.bodyContent: A composable lambda defining the content shown when expanded.
Accordion( headerContent = { Text("Click me to expand") }, bodyContent = { Text("This is the hidden content.") } )Reference: RippleDefaults and State Alpha values
mainThe
RippleDefaultsobject provides the standardRippleAlphaused when no custom alpha is specified. The underlying opacity values used by the system are:PressedStateLayerOpacity: 0.1fFocusStateLayerOpacity: 0.1fDraggedStateLayerOpacity: 0.16fHoverStateLayerOpacity: 0.08f
// Default alpha used by the system val defaultAlpha = RippleDefaults.RippleAlphaImplement PinnedScrollBehavior for TopBars
mainUse
PinnedScrollBehaviorwhen you want the TopBar to remain fixed (pinned) at the top of the screen regardless of scroll position. TheisPinnedproperty is set totrue. It tracks content offset via the providedTopBarStatebut does not modify the bar's height during scrolling.Key parameters:
state: An instance ofTopBarStateto manage the bar's lifecycle.canScroll: A lambda returningBooleanto enable or disable scroll interception (defaults totrue).
val behavior = PinnedScrollBehavior( state = myTopBarState, canScroll = { true } )Implement ExitUntilCollapsedScrollBehavior for TopBars
mainUse
ExitUntilCollapsedScrollBehaviorwhen you want the TopBar to hide as the user scrolls down, but only until it reaches its minimum (collapsed) height. It will stay collapsed while the user continues to scroll down, and only expands when the user scrolls up.Key parameters:
state: An instance ofTopBarState.snapAnimationSpec: AnAnimationSpec<Float>?used to snap the bar to an expanded or collapsed state after a fling.flingAnimationSpec: ADecayAnimationSpec<Float>?used to control the decay animation during a fling.canScroll: A lambda returningBooleanto enable or disable scroll interception (defaults totrue).
val behavior = ExitUntilCollapsedScrollBehavior( state = myTopBarState, snapAnimationSpec = mySnapSpec, flingAnimationSpec = myFlingSpec, canScroll = { true } )Implement EnterAlwaysScrollBehavior for TopBars
mainUse
EnterAlwaysScrollBehaviorwhen you want the TopBar to hide as the user scrolls down and reappear immediately as soon as the user scrolls up. TheisPinnedproperty is set tofalse.Key parameters:
state: An instance ofTopBarState.snapAnimationSpec: AnAnimationSpec<Float>?used to snap the bar to an expanded or collapsed state after a fling.flingAnimationSpec: ADecayAnimationSpec<Float>?used to control the decay animation during a fling.canScroll: A lambda returningBooleanto enable or disable scroll interception (defaults totrue).
val behavior = EnterAlwaysScrollBehavior( state = myTopBarState, snapAnimationSpec = mySnapSpec, flingAnimationSpec = myFlingSpec, canScroll = { true } )Configure TopBarScrollBehavior properties
mainThe
TopBarScrollBehaviorinterface defines the contract for how a TopBar responds to nested scrolling. When implementing or using a behavior, you can access:state: TheTopBarStateinstance being manipulated.isPinned: ABooleanindicating if the bar stays fixed.snapAnimationSpec: An optionalAnimationSpec<Float>?for snapping behavior.flingAnimationSpec: An optionalDecayAnimationSpec<Float>?for fling behavior.nestedScrollConnection: TheNestedScrollConnectionused to intercept scroll events.
Manage Accordion state with AccordionState
mainUse
AccordionStateto programmatically control whether an accordion is expanded, enabled, or clickable. You can create a state instance usingrememberAccordionState().Properties:
expanded: Read-only boolean indicating if the accordion is open.animationProgress: A float (0f to 1f) representing the current animation state.enabled: Boolean indicating if the accordion can be interacted with.clickable: Boolean indicating if the header responds to clicks.
Methods:
toggle(): Switches theexpandedstate ifenabledis true.collapse(): Forces the accordion to close.updateProgress(progress: Float): Updates the internalanimationProgressvalue.
val state = rememberAccordionState(expanded = false) // Later in code... state.toggle() // or state.collapse()