santimattius/performance-compose-skills/compose-views-interop
Claude Code and agent skill by santimattius/performance-compose-skills
- community
Checking that the SKILL.md file is still in the repository.
(Android-only — uses android.view.View) Performance-first View↔Compose interoperability during incremental migration: ComposeView, AndroidView, ViewCompositionStrategy, RecyclerView/ViewPager2 hybrids, Dialog/BottomSheet, Fragment and multi-Activity hosting. Trigger: AndroidView, ComposeView, View interop, custom View in Compose, RecyclerView Compose, hybrid screen, migration coexistence, ViewCompositionStrategy.
Install
npx vskill@latest install santimattius/performance-compose-skills/compose-views-interopSKILL.md
Performance First
Every Compose frame runs three phases. Cost rises left-to-right; restart scope shrinks left-to-right.
| Phase | What Runs | Restart Cost | Trigger | |
|---|---|---|---|---|
| Composition | Composable functions, state reads | HIGH (whole scope) | State read in composable body | |
| Layout | Measure + place | MEDIUM (subtree) | State read in measure lambda | |
| Drawing | Canvas commands, graphicsLayer | LOW (single node) | State read in draw/graphicsLayer lambda |
Interop boundary costs
| Boundary | Extra cost | Phase impact | |
|---|---|---|---|
AndroidView | Real View measure/layout/draw per frame | `update` runs in Composition — any State read there retriggers subtree | |
ComposeView | Separate composition owner + View attach | Wrong ViewCompositionStrategy → leaked compositions or state loss on detach | |
| Hybrid scroll | Two input systems | Layout + jank if nested scroll not coordinated | |
| Mixed APK | Build/size (not runtime by default) | See references/interop-performance-checklist.md |
Rule: at interop boundaries, minimize Composition-phase work; prefer stable parameters and lifecycle-correct disposal.
Performance Toolchain
WARNING: NEVER profile in debug builds. Debug disables R8, inlining, and Strong Skipping — numbers are meaningless. Always profile a release build withprofileableenabled or a benchmark build type.
| Tool | Use For | When | |
|---|---|---|---|
| Layout Inspector (recomposition counts) | Hot AndroidView / parent recomposing on scroll | Hybrid screens flagged in audit | |
| Composition Tracing | Frame spikes at interop nodes | When Layout Inspector shows boundary churn | |
| Macrobenchmark | Scroll/startup on hybrid screens | Per-release regression on RecyclerView/ViewPager2 hosts | |
| Baseline Profiles | AOT paths outside Compose library profile | App-specific View-heavy startup | |
android docs search | Validate interop API claims | Before recommending strategy changes | |
| Compose Compiler Reports | Unstable params into AndroidView update | When update block recomposes every frame |
When to Use
Load this skill when:
- Hosting Compose inside Views (
ComposeView, XML, Fragment, Dialog, RecyclerView ViewHolder, ViewPager2 page) - Embedding Views inside Compose (
AndroidView,AndroidViewBinding, legacy custom Views) - Choosing `ViewCompositionStrategy` for Fragment, Dialog, or pooling containers
- RecyclerView or ViewPager2 mixes Compose and View children
- Single-Activity + Fragments or multi-Activity legacy navigation with partial Compose
- Auditing or fixing performance regressions during incremental migration (not greenfield Compose-only apps)
When NOT to Use
| User intent | Load instead | |
|---|---|---|
| Migrate one XML layout end-to-end (10-step workflow) | Google's migrate-xml-views-to-jetpack-compose | |
Nav3 scenes, AndroidFragment, NavDisplay | compose-navigation-nav3 (v1 interop defers Nav3 — see Follow-up) | |
| Unstable params / skipping inside pure Compose | compose-composition-core | |
LaunchedEffect, DisposableEffect, coroutines at boundary | compose-effects | |
| ViewModel scope, UiState, Screen/Content split | compose-architecture | |
| Accessibility on merged trees | compose-quality | |
| Full project recomposition audit | compose-audit → routes INTEROP-* here |
v1 exclusions (androidx only)
- No Accompanist — use androidx ViewPager2, Compose,
ui-viewbindingonly - No Nav3 `AndroidFragment` / scene recipes — follow-up change
- No CMP desktop View interop
CMP Applicability
Canonical CMP rules: `../_shared/cmp-platform.md`
⚠️ This skill is Android-only. All APIs (AndroidView,ComposeView,ViewCompositionStrategy,RecyclerView,Fragment) useandroid.view.View. There is no CMP equivalent of this skill.
| Source set | Status | Notes | |
|---|---|---|---|
commonMain | ❌ | No android.view.View in commonMain — the entire skill is out of scope | |
androidMain | ✅ | Full skill content applies | |
iosMain | ❌ | Use UIKitView, UIKitViewController, ComposeUIViewController — outside this skill's scope | |
desktopMain | ❌ | Use SwingPanel / SwingInterop — outside this skill's scope | |
wasmJsMain | ❌ | Use HtmlView (experimental) — outside this skill's scope |
Status legend: ✅ fully supported · ⚠️ partial / version-gated · ❌ Android-only.
CMP scope: This skill covers Android View interop ONLY. For iOS embedding (UIKitView, ComposeUIViewController), desktop (SwingPanel), or web (HtmlView), see `../_shared/cmp-platform.md#7-view-interop-across-platforms`.
Interop decision tree
Source of truth at boundary: hoist shared state in ViewModel / UDF holder; avoid two owners mutating the same View property from Compose and View code.
Critical patterns
1. AndroidView — factory once, lean update
See references/android-view-in-compose.md.
2. ComposeView — match ViewCompositionStrategy to host
| Host | Strategy | Why | |
|---|---|---|---|
Fragment onCreateView | DisposeOnViewTreeLifecycleDestroyed | Align with viewLifecycleOwner; avoid state loss on back stack | |
DialogFragment / BottomSheet | DisposeOnViewTreeLifecycleDestroyed | Window lifecycle; see dialog reference | |
RecyclerView ViewHolder | DisposeOnDetachedFromWindowOrReleasedFromPool (default) | Pooling-safe disposal | |
Activity-only ComposeView in XML | Default often OK | Detach from window disposes |
Anti-pattern: calling setContent { } on every onBindViewHolder — use stable content and pass state via parameters. See references/recycler-view-interop.md.
3. Lazy list AndroidView — enable reuse (Compose 1.4+)
4. Window insets — one consumer per hierarchy root
- View-root screen: consume insets in View layer; set
ComposeView.consumeWindowInsets = falsewhen appropriate - Compose-root screen: consume in Compose; pad
AndroidViewwithModifier.windowInsetsPadding
See android docs — insets Views + Compose.
5. State across boundaries
- Compose → View: update block or
SideEffect/ callback from stable hoisted state - View → Compose: listener updates
ViewModel/MutableStateheld above interop node - Never: duplicate source of truth on both sides without a single owner
See references/view-binding-bridge.md and compose-architecture for ViewModel scoping.
6. Dialog / BottomSheet hybrids
Use ComposeView with fragment/dialog lifecycle strategy; avoid retaining composition after dismiss. Material Compose Dialog / ModalBottomSheet can coexist during migration — pick one owner per overlay.
See references/dialog-bottomsheet-hybrids.md.
7. Topology guides
| Topology | Reference | |
|---|---|---|
| Single Activity + Fragment back stack | single-activity-fragments.md | |
| Multiple Activities | multi-activity-patterns.md | |
| RecyclerView ↔ Compose | recycler-view-interop.md | |
| ViewPager2 pages | viewpager2-interop.md |
Anti-patterns (quick reference)
| Anti-pattern | Phase / risk | Fix | |
|---|---|---|---|
AndroidView for Button/Text you can write in Compose | Composition tax forever | Migrate UI to Compose | |
Heavy work in AndroidView update | Composition every state tick | Diff updates; hoist stable ids | |
setContent in RV onBind every bind | Critical jank | One composition per holder; update state | |
| Default strategy in Fragment | State loss / leaks | DisposeOnViewTreeLifecycleDestroyed | |
remember { View() } outside AndroidView | Stale context / leaks | Create in factory | |
| Two scroll parents without nested connection | Layout jank | nestedScroll bridge or single scroller | |
| Profile debug hybrid screen | False conclusions | Release + Macrobenchmark |
Commands
Related skills
| Skill | Relationship | |
|---|---|---|
migrate-xml-views-to-jetpack-compose (Google) | Per-screen migration workflow | |
compose-composition-core | Stability, skipping, deferred reads | |
compose-architecture | ViewModel scope at screen root | |
compose-effects | DisposableEffect for View listeners | |
compose-audit | INTEROP-* patterns route here |
Follow-up (out of v1)
Nav3 bridge: replacing Fragment/ViewPager2 Compose hosts with NavDisplay, scene strategies, and dialog scenes — coordinate with compose-navigation-nav3; do not duplicate Nav3 API docs here.
Resources
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 95 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/compose-views-interop/SKILL.md
Popularity
- 7-day trend score
- 5
Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/santimattius/performance-compose-skills/compose-views-interop)