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¶
- Network Monitor — capture HTTP traffic
- Log Monitor — view app logs
- Preferences — expose typed settings
- Custom Screens — add your own debug screens
- Theming — control the panel's color scheme