Communitygithub.com

lerchenflo/claudeskills

Nutze diese Fähigkeit, um deine Jetpack Compose App auf adaptives Edge-to-Edge umzustellen und häufige Probleme zu beheben. Verwende sie, um UI-Komponenten zu reparieren, die von der Navigations- oder Statusleiste verdeckt werden, IME-Insets zu korrigieren und die Lesbarkeit der Systemleisten sicherzustellen.

Was ist claudeskills?

Diese AI-Agenten-Fähigkeit automatisiert die Migration einer Android Jetpack Compose App auf Edge-to-Edge-Unterstützung und behebt typische Probleme. Sie ist nützlich für Entwickler, die ihre App auf modernes, randloses Design umstellen möchten, ohne manuell alle Insets und Systemleisten-Konflikte zu lösen. Die Fähigkeit unterstützt Plattformen wie Claude Code, Cursor oder Codex und analysiert systematisch Aktivitäten, Komponenten und IME-Szenarien. Sie erstellt einen detaillierten Plan, fügt `enableEdgeToEdge` ein, wendet korrekte Insets über `Scaffold`, `PaddingValues` oder `safeDrawingPadding()` an und behandelt adaptive Scaffolds, IME-Fokus und Kontraste für Navigationsleisten. Die KI stellt sicher, dass Inkonsistenzen vermieden werden (z. B. doppelte Abstände) und bietet Code-Beispiele für richtige und falsche Ansätze. Eine Checkliste am Ende validiert die vollständige Migration.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/lerchenflo/claudeskills/tree/HEAD/android/edge-to-edge

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Voraussetzungen

  • Das Projekt MUSS Android Jetpack Compose verwenden.
  • Das Projekt MUSS auf SDK 35 oder höher abzielen. Falls das SDK niedriger als 35 ist, erhöhe das SDK auf 35.

Schritt 1: Planen

  1. Lokalisiere und analysiere alle Activity-Klassen, um festzustellen, welche bereits Edge-to-Edge-Unterstützung haben. Plane für jede Activity ohne Edge-to-Edge, diese nachzurüsten.
  2. Lokalisiere und analysiere in jeder Activity alle Listen und FAB-Komponenten, um zu erkennen, welche bereits Edge-to-Edge-Unterstützung haben. Für jede Komponente ohne Edge-to-Edge-Unterstützung plane, diese nachzurüsten.
  3. Scanne in jeder Activity nach TextField, OutlinedTextField oder BasicTextField. Falls gefunden, MUSST du überprüfen, dass die IME das Eingabefeld nicht verdeckt, indem du dem IME-Abschnitt dieser Fähigkeit folgst.

Schritt 2: Edge-to-Edge-Unterstützung hinzufügen

  1. Füge enableEdgeToEdge vor setContent in onCreate in jeder Activity hinzu, die nicht bereits enableEdgeToEdge aufruft.
  2. Füge android:windowSoftInputMode="adjustResize" in der AndroidManifest.xml für alle Activities hinzu, die eine Soft-Tastatur verwenden.

Schritt 3: Insets anwenden

  • Die App MUSS System-Insets anwenden oder Inhalte an Lineale ausrichten, damit wichtige UI-Elemente berührbar bleiben. Wähle nur eine Methode, um doppelte Abstände zu vermeiden:

    1. BEVORZUGT: Wenn verfügbar, verwende Scaffolds und übergib PaddingValues an das Content-Lambda.
    Scaffold { innerPadding ->
        // innerPadding berücksichtigt Systemleisten und alle Scaffold-Komponenten
        LazyColumn(
            modifier = Modifier
                .fillMaxSize()
                .consumeWindowInsets(innerPadding),
            contentPadding = innerPadding
        ) { /* Inhalt */ }
    }
    
    1. BEVORZUGT: Wenn verfügbar, verwende die automatische Inset-Behandlung oder Padding-Modifikatoren in Material-Komponenten.

      • Material 3 Komponenten verwalten sichere Bereiche für ihre eigenen Komponenten, einschließlich:
        • TopAppBar
        • SmallTopAppBar
        • CenterAlignedTopAppBar
        • MediumTopAppBar
        • LargeTopAppBar
        • BottomAppBar
        • ModalDrawerSheet
        • DismissibleDrawerSheet
        • PermanentDrawerSheet
        • ModalBottomSheet
        • NavigationBar
        • NavigationRail
      • Für Material 2 Komponenten verwende den windowInsets-Parameter, um Insets manuell für BottomAppBar, TopAppBar und BottomNavigation anzuwenden. WENDE KEIN Padding auf den übergeordneten Container an; übergib stattdessen Insets direkt an die App-Bar-Komponente. Das Anwenden von Padding auf den übergeordneten Container verhindert, dass der App-Bar-Hintergrund in den Systemleistenbereich gezeichnet wird. Wähle für TopAppBar beispielsweise nur eine der folgenden Optionen:
        1. BEVORZUGT: TopAppBar(windowInsets = AppBarDefaults.topAppBarWindowInsets)
        2. TopAppBar(windowInsets = WindowInsets.systemBars.exclude(WindowInsets.navigationBars))
        3. TopAppBar(windowInsets = WindowInsets.systemBars.add(WindowInsets.captionBar))
    2. Für Komponenten außerhalb eines Scaffolds verwende Padding-Modifikatoren wie Modifier.safeDrawingPadding() oder Modifier.windowInsetsPadding(WindowInsets.safeDrawing).

      Box(
          modifier = Modifier
              .fillMaxSize()
              .safeDrawingPadding()
      ) {
          Button(
              onClick = {},
              modifier = Modifier.align(Alignment.BottomCenter)
          ) {
              Text("Login")
          }
      }
      
    3. Für tief verschachtelte Komponenten mit übermäßigem Padding verwende WindowInsetsRulers (z. B. Modifier.fitInside(WindowInsetsRulers.SafeDrawing.current)). Siehe den Abschnitt IME für ein Codebeispiel.

    4. Wenn ein Element (z. B. ein benutzerdefinierter Header oder ein dekorativer Scrim) die Abmessungen einer Systemleiste haben soll, verwende Inset-Größen-Modifikatoren (z. B. Modifier.windowInsetsTopHeight(WindowInsets.systemBars)). Siehe den Abschnitt Listen für ein Codebeispiel.

Adaptive Scaffolds

  • NavigationSuiteScaffold verwaltet sichere Bereiche für eigene Komponenten wie die NavigationRail oder NavigationBar. Adaptive Scaffolds (z. B. NavigationSuiteScaffold, ListDetailPaneScaffold) geben PaddingValues jedoch nicht an ihre inneren Inhalte weiter. Du MUSST Insets auf einzelne Bildschirme oder Komponenten anwenden (z. B. Listen-contentPadding oder FAB-Padding), wie in Schritt 3 beschrieben. WENDE KEIN safeDrawingPadding oder ähnliche Modifikatoren auf das übergeordnete NavigationSuiteScaffold an. Dieses Clipping verhindert einen randlosen Bildschirm.

IME

  • Überprüfe für jede Activity mit einer Soft-Tastatur, ob android:windowSoftInputMode="adjustResize" in der AndroidManifest.xml gesetzt ist. Verwende NICHT SOFT_INPUT_ADJUST_RESIZE, da es veraltet ist. Erhalte dann den Fokus auf das Eingabefeld. Wähle eine Option:
      1. BEVORZUGT: Füge Modifier.fitInside(WindowInsetsRulers.Ime.current) zum Inhaltscontainer hinzu. Dies wird imePadding() vorgezogen, da es Ruckler und zusätzliches Padding reduziert, das durch das Vergessen, Insets weiter oben in der Hierarchie zu konsumieren, verursacht wird.
      1. Füge imePadding zum Inhaltscontainer hinzu. Der Padding-Modifikator MUSS vor Modifier.verticalScroll() platziert werden. Verwende NICHT Modifier.imePadding(), wenn das übergeordnete Element bereits die IME mit contentWindowInsets berücksichtigt (z. B. contentWindowInsets = WindowInsets.safeDrawing). Dies würde zu doppeltem Padding führen.

IMEs mit Scaffolds Codebeispiele

RICHTIG

RICHTIG, weil contentWindowInsets IME-Insets enthält, die als innerPadding an das Content-Lambda übergeben werden.

// RICHTIG
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .consumeWindowInsets(innerPadding)
            .verticalScroll(rememberScrollState())
    ) { /* Inhalt */ }
}

RICHTIG, weil fitInside den Inhalt unabhängig von contentWindowInsets an die IME-Insets anpasst.

// RICHTIG
Scaffold() { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .consumeWindowInsets(innerPadding)
            .fitInside(WindowInsetsRulers.Ime.current)
            .verticalScroll(rememberScrollState())
    ) { /* Inhalt */ }
}

RICHTIG, weil das Standard-contentWindowInsets keine IME-Insets enthält und imePadding() IME-Insets anwendet:

// RICHTIG
Scaffold() { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .consumeWindowInsets(innerPadding)
            .imePadding()
            .verticalScroll(rememberScrollState())
    ) { /* Inhalt */ }
}

FALSCH

FALSCH, weil es überschüssiges Padding gibt, wenn die IME geöffnet wird. IME-Insets werden zweimal angewendet, einmal mit innerPadding, das IME-Insets aus den übergebenen contentWindowInsets-Werten enthält, und einmal mit imePadding:

// FALSCH
Scaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .imePadding()
            .verticalScroll(rememberScrollState())
    ) { /* Inhalt */ }
}

FALSCH, weil die IME den Inhalt verdecken wird. Das Standard-contentWindowInsets des Scaffolds enthält KEINE IME-Insets.

// FALSCH
Scaffold() { innerPadding ->
    Column(
        modifier = Modifier
            .padding(innerPadding)
            .verticalScroll(rememberScrollState())
    ) { /* Inhalt */ }
}

IMEs ohne Scaffolds Codebeispiele

RICHTIG

Die folgenden Codebeispiele werden KEIN übermäßiges Padding verursachen.

// RICHTIG
Box(
    // Insets verbraucht
    modifier = Modifier.safeDrawingPadding() // oder imePadding(), safeContentPadding(), safeGesturesPadding()
) {
    Column(
        modifier = Modifier.imePadding()
    ) { /* Inhalt */ }
}

// RICHTIG
Box(
    // Insets verbraucht
    modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing) // oder WindowInsets.ime, WindowInsets.safeContent, WindowInsets.safeGestures
) {
    Column(
        modifier = Modifier.imePadding()
    ) { /* Inhalt */ }
}

// RICHTIG
Box(
    // Insets nicht verbraucht, aber aufgrund von fitInside irrelevant
    modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // oder WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()
) {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .fitInside(WindowInsetsRulers.Ime.current)
    ) { /* Inhalt */ }
}

FALSCH

Das folgende Codebeispiel wird übermäßiges Padding verursachen, weil IME-Insets zweimal angewendet werden:

// FALSCH
Box(
    // Insets nicht verbraucht
    modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // oder WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()
) {
    Column(
        modifier = Modifier.imePadding()
    ) { /* Inhalt */ }
}

Navigationsleisten-Kontrast und Systemleisten-Symbole

  • Wenn die Activity enableEdgeToEdge aus WindowCompat verwendet, MUSST du isAppearanceLightNavigationBars und isAppearanceLightStatusBars auf die Umkehrung des Gerätethemas setzen, damit die Systemleistensymbole lesbar sind. Es wird empfohlen, dies in deiner Theme-Datei zu tun. TU dies NICHT, wenn die Activities enableEdgeToEdge aus ComponentActivity verwenden, da dies die Icon-Farben automatisch behandelt.

    // Nur verwenden, wenn `enableEdgeToEdge` aus `WindowCompat` aufgerufen wird.
    // In deine Theme-Datei einfügen.
    @Composable
    fun MyTheme(
        darkTheme: Boolean = isSystemInDarkTheme(),
        content: @Composable () -> Unit
    ) {
        val view = LocalView.current
        if (!view.isInEditMode) {
            SideEffect {
                val window = (view.context as? Activity)?.window ?: return@SideEffect
                val controller = WindowCompat.getInsetsController(window, view)
    
                // Dunkle Symbole für hellen Modus (!darkTheme), helle Symbole für dunklen Modus
                controller.isAppearanceLightStatusBars = !darkTheme
                controller.isAppearanceLightNavigationBars = !darkTheme
            }
        }
    
        MaterialTheme(content = content)
    }
    
  • Wenn ein Bildschirm ein Scaffold oder ein NavigationSuiteScaffold mit einer unteren Leiste (z. B. BottomAppBar, NavigationBar) verwendet, setze window.isNavigationBarContrastEnforced = false in der entsprechenden Activity für SDK 29+. Dies verhindert, dass das System einen durchscheinenden Hintergrund zur Navigationsleiste hinzufügt, und stellt sicher, dass die Farben deiner unteren Leiste bis zum unteren Bildschirmrand reichen.

Listen

  • Wende Inset-Padding (wie Scaffold's innerPadding) auf den contentPadding-Parameter von scrollbaren Komponenten an (z. B. LazyColumn, LazyRow). Wende es NICHT als Modifier.padding() auf den übergeordneten Container der Liste an, da dies den Inhalt beschneidet und das Scrollen hinter die Systemleisten verhindert.
  • Erstelle eine durchscheinende Composable, die die Systemleiste abdeckt, damit die Symbole weiterhin lesbar sind.
class SystemBarProtectionSnippets : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // enableEdgeToEdge setzt window.isNavigationBarContrastEnforced = true
        // was verwendet wird, um einen durchscheinenden Scrim zur Drei-Tasten-Navigation hinzuzufügen
        enableEdgeToEdge()

        setContent {
            MyTheme {
                // Hauptinhalt
                MyContent()

                // Nach dem Zeichnen des Hauptinhalts, Statusleisten-Schutz zeichnen
                StatusBarProtection()
            }
        }
    }
}

@Composable
private fun StatusBarProtection(
    color: Color = MaterialTheme.colorScheme.surfaceContainer,
) {
    Spacer(
        modifier = Modifier
            .fillMaxWidth()
            .height(
                with(LocalDensity.current) {
                    (WindowInsets.statusBars.getTop(this) * 1.2f).toDp()
                }
            )
            .background(
                brush = Brush.verticalGradient(
                    colors = listOf(
                        color.copy(alpha = 1f),
                        color.copy(alpha = 0.8f),
                        Color.Transparent
                    )
                )
            )
    )
}

Dialoge

Wenn beide folgenden Bedingungen zutreffen, ist der Dialog Vollbild und muss randlos gemacht werden:

  1. Die DialogProperties enthalten usePlatformDefaultWidth = false.
  2. Der Dialog ruft Modifier.fillMaxSize() auf.

Um einen Vollbild-Dialog randlos zu machen, setze decorFitsSystemWindows = false in den DialogProperties.

Dialog(
    onDismissRequest = { /* Schließen behandeln */ },
    properties = DialogProperties(
        // 1. Ermöglicht dem Dialog, die gesamte Breite des Bildschirms zu nutzen
        usePlatformDefaultWidth = false,
        // 2. Ermöglicht dem Dialog, hinter Status- und Navigationsleisten zu zeichnen
        decorFitsSystemWindows = false
    )
) { /* Inhalt */ }

Checkliste

  • [ ] Ruft jede Activity enableEdgeToEdge() auf?
  • [ ] Ist adjustResize in der AndroidManifest.xml gesetzt?
  • [ ] Hat jedes TextField, OutlinedTextField oder BasicTextField ein übergeordnetes Element mit imePadding(), fitInside, Modifier.safeDrawingPadding(), Modifier.safeContentPadding(), Modifier.safeGesturesPadding() oder contentWindowInsets gesetzt auf WindowInsets.safeDrawing oder WindowInsets.ime?
  • [] Zeichnet das erste und letzte Listenelement mit Abstand zu den Systemleisten, indem Insets an contentPadding übergeben werden?
  • [ ] Zeichnen FABs über den Navigationsleisten, entweder innerhalb eines Scaffolds oder durch Anwenden von Modifier.safeDrawingPadding()?
  • [ ] Baut das Projekt? Führe ./gradlew build aus, um sicherzugehen.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

lerchenflo/claudeskills

Upgrades, or migrates, an Android project to use Android Gradle Plugin (AGP) version 9. Do not use this skill for migrating Kotlin Multiplatform (KMP) projects.

lerchenflo/claudeskills

Provides instructions for installing and using the `android` CLI. The `android` command-line tool is a critical tool for Android development and helps you create new Android projects, run Android apps on devices, manage and interact with Android virtual devices (including screenshots and UI inspection), manage Android SDK components, look up official Android documentation, and discover and install official Android skills.

lerchenflo/claudeskills

Manages Android performance profiling and debugging. Triggers when the user asks to record or analyze Android performance data, such as system traces, heap dumps, method recordings, callstack samples, memory allocations, or investigate bottlenecks, jank, memory leaks, and app startup issues on Android, or when the user asks to write, debug, or execute ad-hoc SQL queries. Applies to both user and system apps or services.

lerchenflo/claudeskills

Analyzes Android apps to identify key user workflows for AppFunctions such as creating a note, playing media, or sending an automated or AI agent triggered message, voice commands, or system shortcuts, without needing to open the app UI. Generates Kotlin code to expose these workflows to the Android system, allowing agents to discover and execute them on-device. Also refines KDoc documentation to ensure AI agents correctly understand and use the provided functionality.

Verwandte Skills