Skip to content

Custom Screens

Wrap any Composable as a first-class debug screen in the Sidekick panel. Use it for feature-flag toggles, environment switchers, internal QA dashboards — anything you'd otherwise jury-rig with a hidden gesture.

Platforms

Android iOS Desktop Web JS Web Wasm

Features

  • Any Composable — render whatever you want; the slot is unrestricted.
  • Full DI access — the content Composable executes inside the host's composition tree, so Koin / Hilt / CompositionLocal providers all work as usual.
  • Many instances — create as many CustomScreenPlugin cards as you need; each appears in the panel grid.
  • No state plumbing — your Composable owns its state via the standard remember / ViewModel patterns.

Modules

Module Purpose
:plugins:custom-screen:api CustomScreenPlugin wrapper (the only thing this plugin needs).

Setup

1. Add dependencies

commonMain.dependencies {
    implementation(platform("dev.parez.sidekick:bom:2026.05.17"))
    implementation("dev.parez.sidekick:custom-screen")
}

2. Wire into Sidekick

val featureFlagsScreen = remember {
    CustomScreenPlugin(
        id    = "com.myapp.feature-flags",
        title = "Feature Flags",
        icon  = Icons.Default.Flag,
    ) {
        // any Composable — DI, ViewModels, CompositionLocals all work here
        FeatureFlagsScreen()
    }
}

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

Create as many instances as you need and pass them all to Sidekick. Because content executes inside the host app's composition tree, DI frameworks (Koin, Hilt, custom CompositionLocals) work without extra wiring.

Configuration

Parameter Description
id Unique identifier across all plugins. Use kebab-case or reverse-domain (e.g. "com.myapp.flags").
title Label shown in the plugin grid card and screen header.
icon ImageVector shown in the plugin grid card.
content Composable rendered when the user opens this screen. Fills the plugin panel.

UI

When the user taps the card in the plugin grid, your content Composable fills the full plugin panel area. The active MaterialTheme is your app's (or Sidekick's, depending on useSidekickTheme). Use Modifier.fillMaxSize() on your root.

Advanced

Many screens at once

A single CustomScreenPlugin represents one card. If you have multiple debug views, create one plugin per view:

val plugins = remember {
    listOf(
        CustomScreenPlugin("com.myapp.flags",       "Feature Flags",  Icons.Default.Flag)        { FeatureFlagsScreen() },
        CustomScreenPlugin("com.myapp.env",         "Environment",    Icons.Default.Public)      { EnvironmentScreen() },
        CustomScreenPlugin("com.myapp.build-info",  "Build Info",     Icons.Default.Info)        { BuildInfoScreen() },
    )
}

Scaffolding with Claude Code

Use the /create-plugin skill to scaffold a full SidekickPlugin module (not just a CustomScreenPlugin wrapper) — useful when your debug screen has enough complexity to warrant its own Gradle module.

See also