К основному содержанию
скилл CX Codex

navigation-event — скилл для Claude Code: что делает и как установить

Помогает внедрить библиотеку androidx.navigationevent в Compose Android. Настраивает предиктивную анимацию жеста «Назад» и заменяет устаревший BackHandler.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add android/skills --skill navigation-event -a claude-code
Открыть на GitHub

Что делает navigation-event

Скилл даёт правила для интеграции библиотеки androidx.navigationevent в Android-проекты на Jetpack Compose. Он объясняет модели жизненного цикла жестов, настраивает NavigationBackHandler и координирует иерархию диспетчеров в ViewPager или вкладках. Если в проекте уже используется Navigation 3, эти инструкции не нужны: там уже есть встроенная поддержка навигации назад.

Для работы требуется compileSdk не ниже 36. Класс ComponentActivity сам реализует интерфейс NavigationEventDispatcherOwner, поэтому переопределять диспетчер вручную нельзя. Попытка объявить собственный navigationEventDispatcher в активити приведёт к бесконечной рекурсии и аварийному падению с ошибкой StackOverflowError. Для диалогов и ModalBottomSheet явный провайдер тоже лишний: они получают диспетчер из коробки.

При работе с табами или пейджерами скилл рекомендует вызывать rememberNavigationEventDispatcherOwner(). Выключение родительского владельца автоматически отключает дочерние обработчики. При этом связка NavigationEventState и обработчика строго один к одному.

Какие задачи решает

  • настроить предиктивный жест «Назад» в Jetpack Compose
  • мигрировать с устаревшего BackHandler на NavigationEvent
  • связать иерархию диспетчеров навигации в ViewPager и вкладках
  • исправить StackOverflowError при переопределении диспетчера в ComponentActivity

Примеры запросов агенту

  • Помоги мигрировать проект на androidx.navigationevent с устаревшего BackHandler
  • Как правильно настроить NavigationBackHandler для диалога в Compose?
  • Свяжи диспетчеры навигации для вложенного ViewPager через rememberNavigationEventDispatcherOwner

Как установить navigation-event

1Через Skills CLI (Vercel)

npx skills add android/skills --skill navigation-event -a claude-code

2Вручную: положить файлы

~/.claude/skills/navigation-event/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/navigation-event/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Из того же набора24

camerax Помогает писать код камеры на Android через библиотеку CameraX. Подсказывает правильные паттерны для записи видео, миграции с Camera2 и работы с ML Kit. CX Codex +1 1 200установок agp-9-upgrade Ведёт Android-проект с AGP 8 на Android Gradle Plugin 9: поднимает зависимости, переводит сборку на встроенный Kotlin и новый DSL. CX Codex +1 1 200установок android-profiler Скилл собирает и разбирает данные о производительности Android: от трейсов Perfetto и утечек памяти до зависаний UI и расхода батареи. CX Codex +1 1 000установок leanback-to-compose-tv-migration Скилл ведёт миграцию Android TV с Leanback и Android Views на Jetpack Compose for TV. Пригодится, когда переписываешь browse, settings или playback экран. CX Codex +1 772установок media3-cast-integration Встраивает поддержку Google Cast в Android-приложения через Jetpack Media3. Настраивает зависимости, манифест, плееры CastPlayer или RemoteCastPlayer и UI на Compose. CX Codex +1 771установок restore-credentials Скилл помогает внедрить Android Restore Credentials через androidx.credentials и вернуть вход после восстановления приложения на новом устройстве. CX Codex +1 638установок android-permissions-security Скилл проверяет Android-проект на дыры в правах и IPC: находит слабые кастомные разрешения, спуфинг вызывающего и лишние запросы. Нужен при ревью AndroidManifest.xml. CX Codex +1 508установок ml-kit-genai-prompt-api Скилл для Android-проектов: помогает подключить ML Kit GenAI Prompt API, разобрать вывод по схеме и настроить кеш префикса. CX Codex +1 442установок

Похожие

mapbox-navigation-patterns mapbox Подсказывает агенту, как собрать пошаговую навигацию, мультистоповые маршруты и голосовые подсказки на Mapbox. CX Codex +1 401установок compose-multiplatform-patterns affaan-m Скилл подсказывает паттерны Compose Multiplatform и Jetpack Compose для KMP-проектов: состояние, навигация, темы и UI под разные платформы. CX Codex +1 4 600установок android-jetpack-compose TheBushidoCollective Помогает агенту писать Android-интерфейсы на Jetpack Compose и выбирать паттерны состояния. Пригодится при работе с composable-функциями и ViewModel. CX Codex +1 2 200установок compose-animations chrisbanes Помогает выбирать и ревьюить анимации в Jetpack Compose под разные сценарии движения. Пригодится при настройке переходов видимости, смене контента и связывании нескольких свойств. CX Codex +1 1 800установок android-design-guidelines platform-design-skills Скилл для ИИ-агента, который держит Android-интерфейс в рамках Material Design 3: динамический цвет, роли цветов, навигация. CX Codex +1 1 700установок compose-expert vitorpamplona Скилл с правилами разработки общего UI на Jetpack Compose в проекте Amethyst: навигация, адаптивная вёрстка и ревью компонентов. CX Codex +1 1 400установок

Рядом в теме «Мобильная разработка»

Всё по теме «Мобильная разработка» →

Вопросы о navigation-event

Как установить navigation-event в Claude Code?

Выполните команду npx skills add android/skills --skill navigation-event -a claude-code. Файл навыка будет сохранён по пути ~/.claude/skills/navigation-event/SKILL.md.

Что делает скилл navigation-event?

Он инструктирует агента, как внедрять библиотеку androidx.navigationevent в Compose Android. Скилл помогает перехватывать жесты возврата, анимировать Predictive Back и изолировать диспетчеры во вкладках.

Почему нельзя переопределять navigationEventDispatcher в MainActivity?

Класс ComponentActivity уже реализует интерфейс диспетчера. Повторное переопределение свойства создаёт циклическое обращение к самому себе и завершается аварийной ошибкой StackOverflowError.

Исходный текст

SKILL.md · Apache-2.0
---
name: navigation-event
description: Intercept back gestures and run Predictive Back animations using the
  NavigationEvent (androidx.navigationevent) library in Compose Android. Handles Activity
  setup, parent-child dispatcher scoping in `ViewPagers` or tabs, Compose `NavigationBackHandler`,
  and migration from legacy `BackHandler` on SDK 36+.
license: Complete terms in LICENSE.txt
metadata:
  author: Google LLC
  last-updated: '2026-10-07'
  keywords:
  - Android
  - Navigation Event
  - Jetpack Compose
  - Back Navigation
  - Dispatcher
  - Guidelines
  - Troubleshooting
  - ComponentActivity
  - Dialog
  - ViewPager
---

## Common guidelines

- **For architecture concepts** : To understand the foundational architecture, continuous gesture event lifecycles, or class definitions of the Navigation Event library, read [Navigation Event overview](references/android/guide/navigation/navigation-event/index.md).
- **For Android target** : If compile SDK is lower than 36, set it to `36` or higher in `build.gradle.kts`.
- **For Compose Android target**: The project must use Jetpack Compose for Compose-specific APIs. This skill is scoped exclusively to Compose Android (Android Views and non-Compose implementations are excluded).
- **For activity dispatchers** : `ComponentActivity` automatically implements `NavigationEventDispatcherOwner` out-of-the-box. You must use the built-in `navigationEventDispatcher` without creating anonymous delegate owners or overriding member properties.
- **For dialog scoping** : Floating windows (Compose `Dialog`, `ModalBottomSheet`, `ComponentDialog`) automatically provide a `NavigationEventDispatcherOwner`. You don't need manual `CompositionLocalProvider` propagation for dialogs.
- **For parent-child dispatcher hierarchies** : When scoping navigation handling to `ViewPagers`, tabbed interfaces, or nested navigation containers in Compose, use `rememberNavigationEventDispatcherOwner()` to create a child owner linked to the parent. Disabling the owner (`enabled = false`) automatically cascades to disable all child handlers.
- **For Compose handlers** : A one-to-one relationship between `NavigationEventState` and handlers is strictly enforced. Never bind the same `NavigationEventState` to multiple active `NavigationBackHandler` instances (`IllegalArgumentException`).

## Step 1: Plan

To complete this step, you **MUST** ensure the following:

1. **Identify the target platform** : Verify the app is targeting Compose Android. If `compileSdk` is lower than 36, set it to `36` or higher in `build.gradle.kts`.
2. **Navigation check**: Check if Navigation 3 is in use. If it is in use, use Navigation 3's built-in back navigation support rather than manually implementing low-level dispatchers from this skill.
3. **Hierarchy check** : Identify host Activities, `ViewPagers`, tabbed interfaces, or nested navigation hosts that require back gesture interception or parent-child dispatcher linking.
4. **Migration check** : Check if the project is migrating from back handling (`OnBackPressedCallback`, `BackHandler`, `onBackPresser`) to `NavigationEvent` and `NavigationBackHandler`.
5. **Input interception** : Detect where the app is intercepting navigation events from gestures or hardware button presses requiring translation to `NavigationEvent`.

## Step 2: Set up dependencies

To complete this step, you **MUST** ensure the following:

- For setting up compile SDKs, declaring catalog versions, and adding dependencies, follow [setup guide](references/android/guide/navigation/navigation-event/setup.md).

## Step 3: Configure dispatcher and inputs

To complete this step, you **MUST** ensure the following:

- To configure your dispatcher, leverage automatic `ComponentActivity` or `ComponentDialog` owner resolution.
- Link parent-child dispatchers in Compose following [dispatcher guide](references/android/guide/navigation/navigation-event/dispatcher.md).

## Step 4: Handle back navigation and UI transitions

To complete this step, you **MUST** ensure the following:

- To create navigation event handlers, integrate back gesture interception in Compose, animate UI components during swipes, and migrate from legacy back handlers, follow [handle back guide](references/android/guide/navigation/navigation-event/handle-back.md).

## Step 5: Clean up resources

> [!WARNING]
> **Warning:** Compose APIs perform teardown automatically. When using Compose APIs such as `NavigationBackHandler` and `rememberNavigationEventDispatcherOwner()`, handler removal and dispatcher disposal occur automatically when the composable leaves the composition.

You **MUST** perform explicit manual cleanup only when managing custom
dispatchers or non-Compose handlers:

- Call `remove()` on active handlers during teardown.
- Call `isEnabled = false` to temporarily disable navigation subtrees.
- Call `dispose()` on dispatcher instances when hosting components are destroyed. Disposing a parent dispatcher automatically cascades to all child dispatchers.

## Core troubleshooting guidelines

### 1. Activity dispatcher setup (StackOverflowError recursion)

`ComponentActivity` implements `NavigationEventDispatcherOwner` automatically
out-of-the-box. Don't override `navigationEventDispatcher` or wrap it in an
anonymous delegate owner.

#### RIGHT

**Why this is RIGHT** : Compose apps use `ComponentActivity` as the host.
`LocalNavigationEventDispatcherOwner.current` automatically resolves the
Activity's built-in dispatcher.


```kotlin
// RIGHT
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationContent()
        }
    }
}
```

<br />

#### WRONG

**Why this is WRONG** : Implementing `NavigationEventDispatcherOwner` directly on
`MainActivity` and overriding `navigationEventDispatcher` with a new instance
shadows the library's extension property, causing a recursive infinite loop
crash on launch (`StackOverflowError`). Creating redundant anonymous delegate
owners (`object : NavigationEventDispatcherOwner`) is unnecessary.


```kotlin
// WRONG
class MainActivity : ComponentActivity(), NavigationEventDispatcherOwner {
    override val navigationEventDispatcher = NavigationEventDispatcher() // Shadow loop crash
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationContent()
        }
    }
}
```

<br />

### 2. Floating window and dialog scoping (automatic ComponentDialog owner)

Floating windows (Compose `Dialog`, `ModalBottomSheet`, and any window backed by
`ComponentDialog`) automatically provide a `NavigationEventDispatcherOwner`.
Don't manually re-provide `LocalNavigationEventDispatcherOwner` using
`CompositionLocalProvider` inside dialogs.

#### RIGHT

**Why this is RIGHT** : `ComponentDialog` handles navigation dispatchers
automatically. Compose `Dialog` components resolve their dispatcher owner
out-of-the-box without manual propagation.


```kotlin
// RIGHT
@Composable
fun MyDialog(onDismiss: () -> Unit) {
    Dialog(onDismissRequest = onDismiss) {
        val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
        NavigationBackHandler(
            state = navigationState,
            onBackCompleted = onDismiss
        )
    }
}
```

<br />

#### WRONG

**Why this is WRONG** : Wrapping dialog content in a manual
`CompositionLocalProvider` creates redundant boilerplate and obscures the
automatic dispatcher resolution provided by `ComponentDialog`.


```kotlin
// WRONG
@Composable
fun MyDialog(onDismiss: () -> Unit) {
    val dispatcherOwner = LocalNavigationEventDispatcherOwner.current!!
    Dialog(onDismissRequest = onDismiss) {
        // Redundant: ComponentDialog provides NavigationEventDispatcherOwner automatically
        CompositionLocalProvider( LocalNavigationEventDispatcherOwner provides dispatcherOwner) {
            val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
            NavigationBackHandler(
                state = navigationState,
                onBackCompleted = onDismiss
            )
        }
    }
}
```

<br />

### 3. Parent-child dispatcher hierarchy (`ViewPagers` and nested navigation)

When managing nested UI hierarchies such as `ViewPagers`, tabbed
interfaces, or custom navigation containers in Compose, use
`rememberNavigationEventDispatcherOwner()` to create a child owner linked to the
composition hierarchy. Setting `enabled = false` on the child owner
automatically disables its dispatcher and all registered child handlers.

#### RIGHT

**Why this is RIGHT** : Using `rememberNavigationEventDispatcherOwner(enabled =
isSelected)` creates a scoped child dispatcher linked to the parent from
`LocalNavigationEventDispatcherOwner.current`. Providing it using
`CompositionLocalProvider` ensures non-visible tabs or pages automatically stop
intercepting back gestures without leaking handlers.


```kotlin
// RIGHT: Scoping child navigation in a ViewPager or Tab interface
@Composable
fun TabPage(isSelected: Boolean) {
    val childOwner = rememberNavigationEventDispatcherOwner(enabled = isSelected)
    CompositionLocalProvider(LocalNavigationEventDispatcherOwner provides childOwner) {
        val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
        NavigationBackHandler(
            state = navigationState,
            onBackCompleted = { /* Handle page back navigation */ }
        )
        // Page content
    }
}
```

<br />

#### WRONG

**Why this is WRONG** : Creating unlinked standalone dispatchers, instantiating
raw dispatchers without remembering them across recompositions, or attempting to
use non-existent methods like `.addChild()` breaks hierarchy routing and leaves
child handlers active even when the page is inactive.


```kotlin
// WRONG
@Composable
fun TabPage(isSelected: Boolean) {
    val parentDispatcher = LocalNavigationEventDispatcherOwner.current?.navigationEventDispatcher
    val childDispatcher = NavigationEventDispatcher() // Unlinked and not remembered across recompositions
    // WRONG: Method does not exist
    parentDispatcher?.addChild(childDispatcher)
}
```

<br />

### 4. Compose multi-handler registration (IllegalArgumentException)

You must not bind the same `NavigationEventState` to multiple active
`NavigationBackHandler` instances, as this throws an `IllegalArgumentException`
at runtime. To handle conditional workflows (such as checking for unsaved
changes versus navigating back immediately), you must register a single unified
handler and branch logic inside `onBackCompleted`.

#### RIGHT

**Why this is RIGHT** : Using a single `NavigationBackHandler` with internal
branching logic inside `onBackCompleted` maintains a strict 1:1 mapping between
`NavigationEventState` and the handler, preventing state collisions.


```kotlin
// RIGHT
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
    state = navigationState,
    isBackEnabled = true,
    onBackCompleted = {
        if (hasUnsavedChanges) {
            showDiscardDialog()
        } else {
            onNavigateUp()
        }
    }
)
```

<br />

#### WRONG

**Why this is WRONG** : Attaching multiple `NavigationBackHandler` composables to
the same `navigationState` instance attempts to bind duplicate handlers to a
single state object, which throws an `IllegalArgumentException` at runtime.


```kotlin
// WRONG
val navigationState = rememberNavigationEventState(currentInfo = NavigationEventInfo.None)
NavigationBackHandler(
    state = navigationState,
    isBackEnabled = hasUnsavedChanges,
    onBackCompleted = { /* Discard changes */ }
)
NavigationBackHandler(
    state = navigationState,
    isBackEnabled = !hasUnsavedChanges,
    onBackCompleted = { /* Navigate up */ }
)
```

<br />

## Checklist

**For Compose Android targets:**

- \[ \] Is compile SDK set to `36` or higher? (If compile SDK is lower than 36, set it to `36` or higher in `build.gradle.kts`).
- \[ \] Is `android:enableOnBackInvokedCallback` NOT explicitly set to `"false"` in `AndroidManifest.xml`? (On API 36+, it defaults to `"true"`; on API 33--35, ensure it is set to `"true"`).
- \[ \] Does the Activity rely on the built-in `ComponentActivity` dispatcher owner without redundant anonymous delegate wrapping?
- \[ \] Do dialogs or sheets rely on automatic `ComponentDialog` dispatcher resolution without redundant `CompositionLocalProvider` wrapping?
- \[ \] Are parent-child dispatcher relationships in Compose scoped using `rememberNavigationEventDispatcherOwner()` when managing nested hierarchies?
- \[ \] Is conditional back logic handled within a single unified `NavigationBackHandler` to avoid duplicate registration (`IllegalArgumentException`)?
- \[ \] Are legacy `BackHandler` usages migrated to `NavigationBackHandler` with predictive progress support?
- \[ \] Does the project build and pass tests successfully?

© Google LLC · Apache-2.0 · android/skills

Смотрите также

Спонсоры проекта

Компании, которые помогают держать аналитику открытой