Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
affaan-m avatar

Compose Multiplatform Patterns

  • 5.6k installs
  • 238k repo stars
  • Updated August 5, 2026
  • affaan-m/everything-claude-code

compose-multiplatform-patterns is an agent skill for Jetpack Compose and Compose Multiplatform UI: ViewModel StateFlow state, navigation, composable design, KMP expect/actual, and performance.

About

The compose-multiplatform-patterns skill teaches Jetpack Compose and Compose Multiplatform patterns for shared UI across Android, iOS, desktop, and web. State management uses a ViewModel with a single screen state data class exposed as StateFlow, collected in Compose via collectAsStateWithLifecycle, plus a sealed event interface replacing many callback lambdas. Navigation covers type-safe Serializable routes in Compose Navigation 2.8+, dialog destinations for confirmations, and passing navigation lambdas instead of NavController into deep composables. Composable design favors slot-based APIs and strict Modifier ordering from layout padding through shape, drawing, and interaction. KMP platform UI uses expect and actual composables for status bar styling. Performance guidance marks stable types with @Immutable, assigns LazyColumn item keys, defers reads with derivedStateOf, and uses remember to avoid recomposition allocations. Theming applies Material 3 dynamic color on Android. Documented anti-patterns reject mutableStateOf in ViewModel when StateFlow exists, heavy work inside composables, and LaunchedEffect(Unit) substituting ViewModel init. References android-clean-architecture f.

  • ViewModel plus single StateFlow screen state collected with collectAsStateWithLifecycle
  • Sealed event sink replaces many per-action callback lambdas on complex screens
  • Type-safe Serializable routes and dialog() destinations in Compose Navigation 2.8+
  • Slot-based composables, Modifier ordering, and expect/actual platform chrome
  • @Immutable models, LazyColumn keys, derivedStateOf, and remember reduce recomposition cost

Compose Multiplatform Patterns by the numbers

  • 5,611 all-time installs (skills.sh)
  • +226 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #30 of 1,039 Mobile Development skills by installs in the Skillselion catalog
  • Security screen: LOW risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

compose-multiplatform-patterns capabilities & compatibility

Capabilities
single stateflow screen state with collectasstat · sealed interface event sink for complex screen i · serializable type safe navhost routes and dialog · slot based composable apis with ordered modifier · kmp expect/actual platform composables and lazyc
Use cases
frontend · ui design
Platforms
macOS · Windows · Linux
IDEs
jetbrains · intellij · vscode · cursor ide
Pricing
Free
From the docs

What compose-multiplatform-patterns says it does

画面状態には単一のデータクラスを使用します。`StateFlow`として公開し、Composeで収集します:
SKILL.md
複雑な画面では、複数のコールバックラムダの代わりにイベント用のシールドインターフェースを使用します:
SKILL.md
Modifierの順序は重要です
SKILL.md
npx skills add https://github.com/affaan-m/everything-claude-code --skill compose-multiplatform-patterns

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs5.6k
repo stars238k
Security audit3 / 3 scanners passed
Last updatedAugust 5, 2026
Repositoryaffaan-m/everything-claude-code

What it does

Build shared Compose UI across Android, iOS, desktop, and web with ViewModel state, navigation, theming, and performance patterns.

Who is it for?

Android and KMP teams building Compose Multiplatform screens with ViewModels, navigation, design systems, and recomposition tuning.

Skip if: Skip when the project is not Compose-based or when backend-only Kotlin work needs no UI layer guidance.

When should I use this skill?

Activate when building Compose or Compose Multiplatform UI, managing ViewModel state in composables, implementing navigation, optimizing recomposition, or designing reusable composables.

What you get

Agents apply single-state ViewModels, sealed events, type-safe navigation, slot composables, and performance tactics for maintainable multiplatform Compose screens.

  • Shared composable modules
  • Navigation and state architecture
  • Themed design-system components

By the numbers

  • Targets four Compose Multiplatform platforms: Android, iOS, Desktop, and Web
  • Covers state management, navigation, theming, and recomposition performance areas

Files

SKILL.mdMarkdownGitHub ↗

Compose Multiplatformパターン

Compose MultiplatformとJetpack Composeを使用して、Android、iOS、デスクトップ、Web間で共有UIを構築するためのパターン。状態管理、ナビゲーション、テーマ設定、パフォーマンスをカバーします。

起動条件

  • Compose UIの構築(Jetpack ComposeまたはCompose Multiplatform)
  • ViewModelとCompose状態によるUI状態の管理
  • KMPまたはAndroidプロジェクトでのナビゲーション実装
  • 再利用可能なコンポーザブルとデザインシステムの設計
  • リコンポジションとレンダリングパフォーマンスの最適化

状態管理

ViewModel + 単一状態オブジェクト

画面状態には単一のデータクラスを使用します。StateFlowとして公開し、Composeで収集します:

data class ItemListState(
    val items: List<Item> = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null,
    val searchQuery: String = ""
)

class ItemListViewModel(
    private val getItems: GetItemsUseCase
) : ViewModel() {
    private val _state = MutableStateFlow(ItemListState())
    val state: StateFlow<ItemListState> = _state.asStateFlow()

    fun onSearch(query: String) {
        _state.update { it.copy(searchQuery = query) }
        loadItems(query)
    }

    private fun loadItems(query: String) {
        viewModelScope.launch {
            _state.update { it.copy(isLoading = true) }
            getItems(query).fold(
                onSuccess = { items -> _state.update { it.copy(items = items, isLoading = false) } },
                onFailure = { e -> _state.update { it.copy(error = e.message, isLoading = false) } }
            )
        }
    }
}

Composeでの状態収集

@Composable
fun ItemListScreen(viewModel: ItemListViewModel = koinViewModel()) {
    val state by viewModel.state.collectAsStateWithLifecycle()

    ItemListContent(
        state = state,
        onSearch = viewModel::onSearch
    )
}

@Composable
private fun ItemListContent(
    state: ItemListState,
    onSearch: (String) -> Unit
) {
    // ステートレスなコンポーザブル — プレビューとテストが容易
}

イベントシンクパターン

複雑な画面では、複数のコールバックラムダの代わりにイベント用のシールドインターフェースを使用します:

sealed interface ItemListEvent {
    data class Search(val query: String) : ItemListEvent
    data class Delete(val itemId: String) : ItemListEvent
    data object Refresh : ItemListEvent
}

// ViewModelの中
fun onEvent(event: ItemListEvent) {
    when (event) {
        is ItemListEvent.Search -> onSearch(event.query)
        is ItemListEvent.Delete -> deleteItem(event.itemId)
        is ItemListEvent.Refresh -> loadItems(_state.value.searchQuery)
    }
}

// コンポーザブルの中 — 多数ではなく単一ラムダ
ItemListContent(
    state = state,
    onEvent = viewModel::onEvent
)

ナビゲーション

型安全なナビゲーション(Compose Navigation 2.8+)

ルートを@Serializableオブジェクトとして定義します:

@Serializable data object HomeRoute
@Serializable data class DetailRoute(val id: String)
@Serializable data object SettingsRoute

@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
    NavHost(navController, startDestination = HomeRoute) {
        composable<HomeRoute> {
            HomeScreen(onNavigateToDetail = { id -> navController.navigate(DetailRoute(id)) })
        }
        composable<DetailRoute> { backStackEntry ->
            val route = backStackEntry.toRoute<DetailRoute>()
            DetailScreen(id = route.id)
        }
        composable<SettingsRoute> { SettingsScreen() }
    }
}

ダイアログとボトムシートナビゲーション

命令型のshow/hideの代わりにdialog()とオーバーレイパターンを使用します:

NavHost(navController, startDestination = HomeRoute) {
    composable<HomeRoute> { /* ... */ }
    dialog<ConfirmDeleteRoute> { backStackEntry ->
        val route = backStackEntry.toRoute<ConfirmDeleteRoute>()
        ConfirmDeleteDialog(
            itemId = route.itemId,
            onConfirm = { navController.popBackStack() },
            onDismiss = { navController.popBackStack() }
        )
    }
}

コンポーザブル設計

スロットベースのAPI

柔軟性のためにスロットパラメータを持つコンポーザブルを設計します:

@Composable
fun AppCard(
    modifier: Modifier = Modifier,
    header: @Composable () -> Unit = {},
    content: @Composable ColumnScope.() -> Unit,
    actions: @Composable RowScope.() -> Unit = {}
) {
    Card(modifier = modifier) {
        Column {
            header()
            Column(content = content)
            Row(horizontalArrangement = Arrangement.End, content = actions)
        }
    }
}

Modifier順序

Modifierの順序は重要です — 以下の順序で適用します:

Text(
    text = "Hello",
    modifier = Modifier
        .padding(16.dp)          // 1. レイアウト(パディング、サイズ)
        .clip(RoundedCornerShape(8.dp))  // 2. 形状
        .background(Color.White) // 3. 描画(背景、ボーダー)
        .clickable { }           // 4. インタラクション
)

KMPプラットフォーム固有のUI

プラットフォームコンポーザブルのexpect/actual

// commonMain
@Composable
expect fun PlatformStatusBar(darkIcons: Boolean)

// androidMain
@Composable
actual fun PlatformStatusBar(darkIcons: Boolean) {
    val systemUiController = rememberSystemUiController()
    SideEffect { systemUiController.setStatusBarColor(Color.Transparent, darkIcons) }
}

// iosMain
@Composable
actual fun PlatformStatusBar(darkIcons: Boolean) {
    // iOSはUIKitインターロップまたはInfo.plistで処理
}

パフォーマンス

スキップ可能なリコンポジションのための安定した型

すべてのプロパティが安定している場合、クラスを@Stableまたは@Immutableでマークします:

@Immutable
data class ItemUiModel(
    val id: String,
    val title: String,
    val description: String,
    val progress: Float
)

key()と遅延リストの正しい使用

LazyColumn {
    items(
        items = items,
        key = { it.id }  // 安定したキーによりアイテムの再利用とアニメーションが可能
    ) { item ->
        ItemRow(item = item)
    }
}

derivedStateOfで読み取りを遅延

val listState = rememberLazyListState()
val showScrollToTop by remember {
    derivedStateOf { listState.firstVisibleItemIndex > 5 }
}

リコンポジションでのアロケーションを避ける

// 悪い例 — リコンポジションのたびに新しいラムダとリストが作られる
items.filter { it.isActive }.forEach { ActiveItem(it, onClick = { handle(it) }) }

// 良い例 — 各アイテムにキーを付けてコールバックが正しい行に紐づくようにする
val activeItems = remember(items) { items.filter { it.isActive } }
activeItems.forEach { item ->
    key(item.id) {
        ActiveItem(item, onClick = { handle(item) })
    }
}

テーマ設定

Material 3ダイナミックテーマ

@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            if (darkTheme) dynamicDarkColorScheme(LocalContext.current)
            else dynamicLightColorScheme(LocalContext.current)
        }
        darkTheme -> darkColorScheme()
        else -> lightColorScheme()
    }

    MaterialTheme(colorScheme = colorScheme, content = content)
}

避けるべきアンチパターン

  • ライフサイクルに対してより安全なcollectAsStateWithLifecycleを使用したMutableStateFlowがある場合にViewModelでmutableStateOfを使用すること
  • コンポーザブルの深い階層にNavControllerを渡すこと — 代わりにラムダコールバックを渡す
  • @Composable関数内の重い計算 — ViewModelかremember {}に移動する
  • 一部の設定では設定変更のたびに再実行されるため、ViewModel initの代替としてLaunchedEffect(Unit)を使用すること
  • コンポーザブルのパラメータに新しいオブジェクトインスタンスを作成すること — 不必要なリコンポジションを引き起こす

参照

スキル: モジュール構造とレイヤーについてはandroid-clean-architectureを参照。 スキル: コルーチンとFlowパターンについてはkotlin-coroutines-flowsを参照。

Related skills

Forks & variants (1)

Compose Multiplatform Patterns has 1 known copy in the catalog totaling 1.4k installs. They canonicalize to this original listing.

How it compares

Choose compose-multiplatform-patterns over kotlin-patterns when the task is shared UI architecture rather than language idioms or Gradle configuration.

FAQ

How should screen state be exposed to composables?

Use one data class per screen in the ViewModel, expose it as StateFlow, and collect with collectAsStateWithLifecycle in the composable entry point.

What navigation pattern does the skill recommend?

Define Serializable route types, host them in NavHost 2.8+, use dialog destinations for overlays, and pass navigate lambdas instead of NavController deep in the tree.

How do KMP apps handle platform-specific UI chrome?

Declare expect composables in commonMain and provide actual implementations per platform, such as PlatformStatusBar on Android and iOS.

Is Compose Multiplatform Patterns safe to install?

skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.