lumo-ui

repository·main·Indexed 20 days ago

https://github.com/nomanr/lumo-ui

A 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.

Tokens
2.6K
Snippets
10
Records
12
Agent score
69%

What's inside lumo-ui

  1. Overview of lumo-ui

    main
    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.
  2. Explore lumo-ui components via the sample app

    main

    To 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.apk
  3. Manage multiple accordions with AccordionGroupState

    main

    When 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 allowMultipleOpen is false (default), expanding one accordion will automatically collapse all others in the group.
    • Multiple Expansion Mode: If allowMultipleOpen is true, multiple accordions can be open simultaneously.

    Methods:

    • getState(index: Int): Returns the AccordionState for the specific accordion at that index. This state must be passed to the Accordion component 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") }
        )
    }
  4. Configure ripple effects with ripple()

    main

    Use the ripple() function to create an IndicationNodeFactory for visual feedback during interactions. You can customize the ripple's boundary, radius, and color.

    There are two main overloads:

    1. Static Color: Pass a fixed Color.
    2. Dynamic Color: Pass a ColorProducer for colors that can change dynamically (e.g., based on theme or state).

    Parameters:

    • bounded: If true, the ripple is constrained to the component's bounds. Defaults to true.
    • radius: The radius of the ripple. If Dp.Unspecified, it uses the default behavior.
    • color: The color of the ripple. If Color.Unspecified, it falls back to LocalContentColor or the color provided in LocalRippleConfiguration.
    // 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)
  5. Customize ripple behavior via LocalRippleConfiguration

    main

    You can provide a global or scoped RippleConfiguration using CompositionLocalProvider with LocalRippleConfiguration. This allows you to set a default color and alpha for all ripples within a specific UI subtree.

    RippleConfiguration properties:

    • color: A Color used if the specific ripple instance uses Color.Unspecified.
    • rippleAlpha: A RippleAlpha object defining the opacity for different interaction states (pressed, focused, dragged, hovered). If null, RippleDefaults.RippleAlpha is 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
    }
  6. Use the Accordion component

    main

    The Accordion component is a Compose UI element that displays a header and an expandable body. It supports optional animation and can be controlled via an AccordionState object.

    Parameters:

    • modifier: Modifier for the outer container.
    • headerModifier: Modifier for the header area.
    • state: An AccordionState instance (use rememberAccordionState() for default behavior).
    • animate: Boolean to enable/disable vertical expansion/shrink animations (defaults to true).
    • interactionSource: MutableInteractionSource for 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.")
        }
    )
  7. Reference: RippleDefaults and State Alpha values

    main

    The RippleDefaults object provides the standard RippleAlpha used when no custom alpha is specified. The underlying opacity values used by the system are:

    • PressedStateLayerOpacity: 0.1f
    • FocusStateLayerOpacity: 0.1f
    • DraggedStateLayerOpacity: 0.16f
    • HoverStateLayerOpacity: 0.08f
    // Default alpha used by the system
    val defaultAlpha = RippleDefaults.RippleAlpha
  8. Implement PinnedScrollBehavior for TopBars

    main

    Use PinnedScrollBehavior when you want the TopBar to remain fixed (pinned) at the top of the screen regardless of scroll position. The isPinned property is set to true. It tracks content offset via the provided TopBarState but does not modify the bar's height during scrolling.

    Key parameters:

    • state: An instance of TopBarState to manage the bar's lifecycle.
    • canScroll: A lambda returning Boolean to enable or disable scroll interception (defaults to true).
    val behavior = PinnedScrollBehavior(
        state = myTopBarState,
        canScroll = { true }
    )
  9. Implement ExitUntilCollapsedScrollBehavior for TopBars

    main

    Use ExitUntilCollapsedScrollBehavior when 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 of TopBarState.
    • snapAnimationSpec: An AnimationSpec<Float>? used to snap the bar to an expanded or collapsed state after a fling.
    • flingAnimationSpec: A DecayAnimationSpec<Float>? used to control the decay animation during a fling.
    • canScroll: A lambda returning Boolean to enable or disable scroll interception (defaults to true).
    val behavior = ExitUntilCollapsedScrollBehavior(
        state = myTopBarState,
        snapAnimationSpec = mySnapSpec,
        flingAnimationSpec = myFlingSpec,
        canScroll = { true }
    )
  10. Implement EnterAlwaysScrollBehavior for TopBars

    main

    Use EnterAlwaysScrollBehavior when you want the TopBar to hide as the user scrolls down and reappear immediately as soon as the user scrolls up. The isPinned property is set to false.

    Key parameters:

    • state: An instance of TopBarState.
    • snapAnimationSpec: An AnimationSpec<Float>? used to snap the bar to an expanded or collapsed state after a fling.
    • flingAnimationSpec: A DecayAnimationSpec<Float>? used to control the decay animation during a fling.
    • canScroll: A lambda returning Boolean to enable or disable scroll interception (defaults to true).
    val behavior = EnterAlwaysScrollBehavior(
        state = myTopBarState,
        snapAnimationSpec = mySnapSpec,
        flingAnimationSpec = myFlingSpec,
        canScroll = { true }
    )
  11. Configure TopBarScrollBehavior properties

    main

    The TopBarScrollBehavior interface defines the contract for how a TopBar responds to nested scrolling. When implementing or using a behavior, you can access:

    • state: The TopBarState instance being manipulated.
    • isPinned: A Boolean indicating if the bar stays fixed.
    • snapAnimationSpec: An optional AnimationSpec<Float>? for snapping behavior.
    • flingAnimationSpec: An optional DecayAnimationSpec<Float>? for fling behavior.
    • nestedScrollConnection: The NestedScrollConnection used to intercept scroll events.
  12. Manage Accordion state with AccordionState

    main

    Use AccordionState to programmatically control whether an accordion is expanded, enabled, or clickable. You can create a state instance using rememberAccordionState().

    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 the expanded state if enabled is true.
    • collapse(): Forces the accordion to close.
    • updateProgress(progress: Float): Updates the internal animationProgress value.
    val state = rememberAccordionState(expanded = false)
    
    // Later in code...
    state.toggle()
    // or
    state.collapse()