AzNavRail

A contemptably stubborn if not dictatorially restrictive navigation rail/menu–I call it a renu. Or maybe a mail. No, a navigrenuail–for Jetpack Compose with a streamlined, DSL-style API.

This “navigrenuail” provides a vertical navigation rail that expands to a full menu drawer. It is designed to be “batteries-included,” providing common behaviors and features out-of-the-box to ensure a consistent look and feel across applications.


📚 Documentation


🚀 Setup

Add JitPack to your settings.gradle.kts:

Features

AzNavRail for Android (Jetpack Compose)

Setup

To use this library, add JitPack to your settings.gradle.kts:

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        mavenCentral()
        maven { url = uri("https://jitpack.io") }
    }
}

Add the dependency to your app’s build.gradle.kts:

dependencies {
    implementation("com.github.HereLiesAz:AzNavRail:VERSION") // Replace VERSION with the latest release
}

🛠️ The Golden Sample

This is the standard, validated way to initialize AzNavRail. It must be wrapped in AzHostActivityLayout.

import com.hereliesaz.aznavrail.*

@Composable
fun SampleScreen() {
    val navController = rememberNavController()

    AzHostActivityLayout(
        navController = navController,
        initiallyExpanded = false
    ) {
        // 1. CONFIGURATION
        azConfig(
            dockingSide = AzDockingSide.LEFT,
            packButtons = true, // Tightly pack rail items
            displayAppName = true
        )

        azTheme(activeColor = Color.Cyan)

        // 2. NAVIGATION ITEMS
        azRailItem(id = "home", text = "Home", route = "home", content = Icons.Default.Home)
        azRailItem(id = "profile", text = "Profile", route = "profile")

        // 3. MENU ONLY ITEMS
        azMenuItem(id = "settings", text = "Settings", route = "settings")

        // 4. ONSCREEN CONTENT
        // Use 'onscreen' to define your UI. 
        // Layout rules (safe zones, padding) are enforced automatically.
        onscreen(alignment = Alignment.Center) {
            AzNavHost(startDestination = "home") {
                composable("home") { Text("Home Screen") }
                composable("profile") { Text("Profile Screen") }
                composable("settings") { Text("Settings Screen") }
            }
        }
    }
}

✨ Features & Basics

AzHostActivityLayout Configuration

AzHostActivityLayout accepts several parameters to customize its behavior:

AzHostActivityLayout Layout Rules

AzHostActivityLayout enforces a “Strict Mode” layout system:

  1. Rail Avoidance: No content in the onscreen block will overlap the rail. Padding is automatically applied based on the docking side.
  2. Vertical Safe Zones: Content is restricted from the top 10% and bottom 10% of the screen.
  3. Automatic Flipping: Alignments passed to onscreen (e.g., TopStart) are automatically mirrored if the rail is docked to the right.
  4. Backgrounds: Use the background(weight) DSL to place full-screen content behind the UI (e.g., maps, camera feeds). Backgrounds ignore safe zones.

Help Overlay (Help Mode)

AzNavRail includes an interactive “Help Mode” (formerly Info Screen), ideal for onboarding or help sections.

AzTextBox and AzForm

AzTextBox is a text input field. AzForm is a container that groups multiple AzTextBox fields, managing them as a single entity with one submit button.

Features

AzLoad Animation

React Quick Start

import { AzNavRail, AzNavItem, AzButtonShape, AzDockingSide } from '@HereLiesAz/aznavrail-react';

export default function App() {
  const [expanded, setExpanded] = useState(false);

  const items: AzNavItem[] = [
    {
      id: "home",
      text: "Home",
      isRailItem: true,
      onClick: () => console.log("Home clicked"),
      shape: AzButtonShape.CIRCLE,
    },
    {
      id: "settings",
      text: "Settings",
      isRailItem: true,
      onClick: () => console.log("Settings clicked"),
      shape: AzButtonShape.RECTANGLE,
    }
  ];

  return (
    <View style=>
      <AzNavRail
        appName="My App"
        appIcon={require('./assets/icon.png')}
        items={items}
        expanded={expanded}
        onToggleExpand={() => setExpanded(!expanded)}
        settings=
      />
      <View style=>
        {/* Main Content */}
      </View>
    </View>
  );
}

1. Strict Layout System

AzHostActivityLayout enforces a “Constitution” for your UI to ensure consistency and usability:

2. Navigation Items

3. Interactive Components

Manage state directly in the rail without leaving the context.

AzNavRail(...) {
    azAdvanced(
        isLoading = true // Shows the AzLoad animation in the center of the screen
        // ...
    )
}

React Implementation:

import { AzNavItem } from '@HereLiesAz/aznavrail-react';

const items: AzNavItem[] = [
    {
        id: "power",
        isRailItem: true,
        isToggle: true,
        isChecked: isPowerOn,
        toggleOnText: "Power On",
        toggleOffText: "Power Off",
        onClick: () => setPowerOn(!isPowerOn),
        // ...
    },
    {
        id: "mode",
        isRailItem: true,
        isCycler: true,
        options: ["Auto", "Cool", "Heat"],
        selectedOption: currentMode,
        // ...
    }
];

Standalone Usage

You can also use AzLoad directly in your composables.

React Implementation:

import { AzLoad } from '@HereLiesAz/aznavrail-react';

<AzLoad size={48} color="#6200EE" />

Standalone Buttons

The AzButton component (and AzToggle, AzCycler) can be used independently of the rail.

AzButton(
    onClick = { /* ... */ },
    text = "Save",
    modifier = Modifier.fillMaxWidth(), // Now supports modifiers
    shape = AzButtonShape.RECTANGLE,
    enabled = true, // Can be disabled
    isLoading = false, // Shows loading spinner without resizing button
    contentPadding = PaddingValues(16.dp) // Custom padding
)

AzRoller

The AzRoller component is a versatile dropdown that behaves like a slot machine but also supports typing and filtering. It extends the functionality of AzTextBox with a unique split-click interaction model.

AzRoller(
    options = listOf("Cherry", "Bell", "Bar"),
    selectedOption = "Cherry",
    onOptionSelected = { /* handle selection (String) */ },
    hint = "Select Item",
    enabled = true,
    isError = false
)

React Implementation:

import { AzRoller } from '@HereLiesAz/aznavrail-react';

<AzRoller
    options={["Cherry", "Bell", "Bar"]}
    selectedOption="Cherry"
    onOptionSelected={(option) => { /* handle selection */ }}
    hint="Select Item"
    enabled={true}
/>

Hierarchical Navigation

AzNavRail supports hierarchical navigation with host and sub-items. This allows you to create nested menus that are easy to navigate.

React Implementation:

const items: AzNavItem[] = [
    {
        id: "host-1",
        text: "Host Item",
        isRailItem: true,
        isHost: true,
        isExpanded: isHost1Expanded,
        onClick: () => setHost1Expanded(!isHost1Expanded),
        // ...
    },
    {
        id: "sub-1",
        text: "Sub Item",
        isRailItem: true,
        isSubItem: true,
        hostId: "host-1",
        // ...
    }
];

Draggable Rail (FAB Mode)

The rail can be detached and moved around the screen by long-pressing the header icon, which activates “FAB Mode”. To enable this feature, set enableRailDragging = true in the azAdvanced block.

React Implementation:

import { AzNavRailSettings } from '@HereLiesAz/aznavrail-react';

const settings: AzNavRailSettings = {
    enableRailDragging: true
};
// Pass settings to AzNavRail

Reorderable Items (AzRailRelocItem)

AzRailRelocItem is a specialized sub-item that users can reorder via drag-and-drop. This feature is supported on Android, Web, and React Native.

azRailRelocItem(
    id = "reloc-1",
    hostId = "host-1",
    text = "Item 1",
    forceHiddenMenuOpen = false, // Programmatically open the hidden menu!
    onHiddenMenuDismiss = { /* Menu dismissed */ },
    onRelocate = { from, to, newOrder ->
        // Handle new order (List<String>)
    }
) {
    // Hidden Menu (Tap to select -> Long Press to open)
    listItem("Action 1") { /* ... */ }
    inputItem("Rename", initialValue = "Item 1") { newName -> /* ... */ }
}

React Implementation:

import { AzRailRelocItemProps, HiddenMenuScope } from '@HereLiesAz/aznavrail-react';

const relocItem: AzRailRelocItemProps = {
    id: "reloc-1",
    hostId: "host-1",
    text: "Item 1",
    isRailItem: false,
    isSubItem: true,
    forceHiddenMenuOpen: false,
    onHiddenMenuDismiss: () => { /* Menu dismissed */ },
    onRelocate: (fromIndex, toIndex, newOrder) => {
        // Handle new order
    },
    hiddenMenu: (scope: HiddenMenuScope) => {
        scope.listItem("Action 1", () => { /* ... */ });
        scope.inputItem("Rename", "Item 1", (newName) => { /* ... */ });
    }
};
// Pass this object within the items array to AzNavRail

System Overlay

AzNavRail can function as a system-wide overlay (using SYSTEM_ALERT_WINDOW). This allows users to access the navigation menu from anywhere on their device.

Features


Theming and Customization

The expanded menu text font size (and the footer items text size) is strictly controlled by your app’s MaterialTheme.typography.titleLarge. To adjust the text size inside the side menu drawer, simply customize the titleLarge attribute in your app’s typography theme!

Customizing Item Text and Colors

Navigation items support overriding their display text and colors when shown in the menu versus the rail using menuText, menuToggleOnText, menuToggleOffText, menuOptions, textColor, and fillColor properties! By default, the fillColor (translucent background) is automatically computed to be Black (with 25% opacity), unless the item’s main color is Black, in which case it is set to White (with 25% opacity) to ensure proper contrast.

React Implementation:

// Fonts and colors can be passed as props directly.
// The expanded menu text size can be handled via CSS or React Native styles
// depending on your implementation environment.
const settings: AzNavRailSettings = {
    activeColor: '#6200EE'
};

const items: AzNavItem[] = [
    {
        id: "custom",
        text: "Custom Color",
        color: '#FF0000',
        textColor: '#FFFFFF',
        fillColor: 'rgba(255,0,0,0.25)',
        // ...
    }
];

Documentation

The library includes a comprehensive Complete Guide (docs/AZNAVRAIL_COMPLETE_GUIDE.md) containing:

License

Copyright 2024 The AzNavRail Authors

Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Tutorial Framework

AzNavRail features a powerful tutorial framework allowing you to script interactive scenes, dim the screen, and highlight specific items via an easy-to-use DSL. Tutorials are passed in azAdvanced or azSettings. When a tutorial is associated with an item ID, tapping that item’s Help card will launch the tutorial sequence.

import com.hereliesaz.aznavrail.tutorial.AzHighlight
import com.hereliesaz.aznavrail.tutorial.azTutorial

azAdvanced(
    helpEnabled = true,
    tutorials = mapOf(
        "my-item-id" to azTutorial {
            // A scene displays custom composable content underneath the tutorial overlay
            scene(
                id = "scene1",
                content = {
                    Box(Modifier.fillMaxSize().background(Color.DarkGray)) {
                        Text("Scripted App Screen", color = Color.White)
                    }
                }
            ) {
                // Cards display textual instructions with next/skip actions
                card(
                    title = "Welcome",
                    text = "Welcome to the tutorial.",
                    highlight = AzHighlight.FullScreen
                )
                card(
                    title = "Highlighting",
                    text = "Notice the highlighted item.",
                    highlight = AzHighlight.Item("my-item-id"),
                    actionText = "Finish"
                )
            }
        }
    )
)

React Implementation:

import { AzNavRailSettings } from '@HereLiesAz/aznavrail-react';

const settings: AzNavRailSettings = {
    infoScreen: true,
    helpList: {
        "my-item-id": "This item has extended help information."
    },
    onDismissInfoScreen: () => { /* Handle dismissal */ }
};
// Pass settings to AzNavRail