Skip to content

Quick Start

After installing the core module, add Sidekick to your root composable. The client app owns the FAB and visibility state — Sidekick only renders the debug panel itself, and accepts an actions slot where you typically place a close button.

@Composable
fun App() {
    val networkPlugin = remember { NetworkMonitorPlugin() }
    val plugins = remember(networkPlugin) { listOf(networkPlugin) }

    var sidekickVisible by remember { mutableStateOf(false) }

    MaterialTheme {
        Box(Modifier.fillMaxSize()) {
            // your app content
            MyAppContent()

            // FAB to open Sidekick
            SmallFloatingActionButton(
                onClick = { sidekickVisible = true },
                modifier = Modifier.align(Alignment.BottomEnd).padding(16.dp),
            ) {
                Icon(Icons.Default.BugReport, contentDescription = "Open Sidekick")
            }

            // Sidekick panel
            AnimatedVisibility(
                visible = sidekickVisible,
                enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
                exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(),
            ) {
                Sidekick(
                    plugins = plugins,
                    actions = {
                        IconButton(onClick = { sidekickVisible = false }) {
                            Icon(Icons.Default.Close, contentDescription = "Close")
                        }
                    },
                )
            }
        }
    }
}

Visibility is yours to control

You can use any trigger — a shake gesture, a secret tap zone, a build-type check. Sidekick is just a composable; show it however you like.


Adding More Plugins

Pass any combination of plugins:

val networkPlugin = remember { NetworkMonitorPlugin() }
val logPlugin = remember { LogMonitorPlugin() }
val prefsPlugin = remember { AppPreferencesPlugin() } // generated by KSP

Sidekick(
    plugins = listOf(networkPlugin, logPlugin, prefsPlugin),
    actions = {
        IconButton(onClick = { sidekickVisible = false }) {
            Icon(Icons.Default.Close, contentDescription = "Close")
        }
    },
)

Each plugin appears as a card in the panel grid. Tap a card to open that plugin's screen.


Customising the Panel Header

Sidekick() exposes a few optional slots and parameters for the plugin-list app bar.

Sidekick(
    plugins = plugins,
    title = "Debug Tools",                  // app-bar title
    navigationIcon = {                      // leading slot
        Icon(Icons.Default.Construction, contentDescription = null)
    },
    actions = {                             // trailing slot (usually a close button)
        IconButton(onClick = { sidekickVisible = false }) {
            Icon(Icons.Default.Close, contentDescription = "Close")
        }
    },
    appInfo = SidekickAppInfo.detect()      // optional host metadata block
        .withExtras("Environment" to "staging"),
)
Parameter Default Purpose
title "Sidekick" Title shown in the plugin-list app bar.
navigationIcon empty slot Leading slot on the plugin-list app bar.
actions empty slot Trailing slot — wire your close button here.
appInfo auto-detected Host metadata (app name, version, build type, platform). Pass null to hide the header chip.
useSidekickTheme true When false, inherits the host's ambient MaterialTheme. See Theming.

SidekickAppInfo.detect() auto-fills useful host metadata on every platform:

Platform Auto-detected fields
Android app name, version, build code, build type, API level, device, manufacturer
iOS app name, version, build code, iOS version, device model
Desktop (JVM) OS name/version, JVM version, processor count
Web browser user-agent + parsed browser name

Use .withExtras("key" to "value", ...) to append custom badges (e.g. environment, region) without losing the auto-detected fields.


Next Steps