本文へ移動
cccskills
無料GitHub で公開

android-compose-patterns

RIPDPI's own Compose conventions: ViewModel pattern, Route/Screen split, DataStore->StateFlow, RipDpiThemeTokens. Use for how this app does Compose, not generic Compose API questions (see compose).

インストール方法を見る

含まれるファイル(1)

  • SKILL.md5.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Android Compose Patterns

Overview

RIPDPI uses Jetpack Compose with Material 3 and a sealed-class navigation system. State flows from DataStore through ViewModels to Composables via StateFlow + collectAsStateWithLifecycle().

Diagnostics UI is a current hotspot in this repo. It layers internal UI models over shared diagnostics contracts, exposes stable automation tags through RipDpiTestTags, and routes callback-style actions such as opening Advanced Settings or candidate-detail sheets through screen-level parameters.

Data Flow

DataStore (proto) -> ViewModel (StateFlow + combine) -> Composable (collectAsStateWithLifecycle)
User action -> ViewModel method -> DataStore.updateData { } or ServiceManager.start/stop
One-shot effects -> Channel<Effect> -> receiveAsFlow() -> LaunchedEffect collector

Navigation

Routes are defined as a sealed class in app/.../ui/navigation/Route.kt.

Adding a New Screen

  1. Add route to Route sealed class with route string, @StringRes titleRes, optional icon
  2. If top-level: add to Route.topLevel list
  3. Add to Route.all list
  4. Add composable(Route.YourRoute.route) { ... } in RipDpiNavHost.kt
  5. Navigate: navController.navigate(Route.YourRoute.route) { launchSingleTop = true; restoreState = true }

Navigation Conventions

  • Use launchSingleTop = true and restoreState = true for all navigations
  • Bottom bar shows only for isTopLevelDestination() routes
  • Pass ViewModels to route composables, not raw state

ViewModel Pattern

class ExampleViewModel(application: Application) : AndroidViewModel(application) {
    // State: combine multiple sources into single UI state
    val uiState: StateFlow<UiState> = combine(
        application.settingsStore.data,
        _localState,
    ) { settings, local -> UiState(/*...*/) }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), UiState())

    // Effects: one-shot events (navigation, snackbar, permission requests)
    private val _effects = Channel<Effect>(Channel.BUFFERED)
    val effects = _effects.receiveAsFlow()

    // Actions: public methods called by Composables
    fun onAction() { viewModelScope.launch { /* ... */ } }
}

Key Conventions

  • SharingStarted.WhileSubscribed(5_000) for all StateFlow exports
  • AndroidViewModel (not plain ViewModel) when DataStore access needed
  • Mutex for thread-safe state transitions (see MainViewModel.toggleService)
  • Channel<Effect> for one-shot UI events, collected via LaunchedEffect

Composable Pattern

// Route composable: connects ViewModel to screen
@Composable
fun ExampleRoute(viewModel: ExampleViewModel, navController: NavController) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    ExampleScreen(uiState = uiState, onAction = viewModel::onAction)
}

// Screen composable: pure UI, no ViewModel reference
@Composable
fun ExampleScreen(uiState: UiState, onAction: () -> Unit) { /* ... */ }

Conventions

  • Split Route (stateful) from Screen (stateless) composables
  • Use collectAsStateWithLifecycle() (not collectAsState())
  • Pass callbacks, not ViewModel references, to Screen composables
  • Use RipDpiThemeTokens for colors, spacing, typography

Representative ViewModels

ViewModelLocationPurpose
MainViewModelactivities/MainViewModel.ktConnection state, VPN/proxy toggle, metrics
ConfigViewModelactivities/ConfigViewModel.ktProxy config presets, draft editing, validation
SettingsViewModelactivities/SettingsViewModel.ktApp settings, theme, DataStore persistence
DiagnosticsViewModelactivities/DiagnosticsViewModel.ktDiagnostics scan orchestration, history, export/share state, and strategy-probe presentation

Diagnostics UI Conventions

  • Keep diagnostics projection logic in the activities/DiagnosticsUi* support files instead of recomputing report metadata directly in composables.
  • Strategy-probe screens now have specialized presentation states such as candidate-aware progress, audit assessment, winners-first layout, and workflow restriction remediation.
  • Prefer stable automation tags from app/src/main/kotlin/com/poyka/ripdpi/ui/testing/RipDpiTestTags.kt for any new externally exercised UI.
  • Route navigation callbacks such as onOpenAdvancedSettings, onSelectCandidate, and sheet-dismiss actions through Route/Screen parameters rather than letting deep child composables navigate directly.

Common Mistakes

MistakeFix
Using collectAsState()Use collectAsStateWithLifecycle() for lifecycle awareness
Passing ViewModel to Screen composablePass uiState and callbacks; keep Screen stateless
Hardcoding colors/spacingUse RipDpiThemeTokens and Material 3 theme
Missing WhileSubscribed(5_000)Required for proper lifecycle handling in all StateFlow exports
Creating ViewModel in ComposableUse viewModel() in Route composable or pass from NavHost

Related Skills

  • compose (.agents/skills/compose/SKILL.md): General Jetpack Compose API reference (guidance docs plus a live-fetch index into the actual androidx source) and the scored RIPDPI Compose quality audit. Use for questions about how Compose APIs work internally, correct API usage patterns, recomposition mechanics, Modifier ordering, side-effects, performance, or accessibility.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

ADB-based RIPDPI device/emulator debugging: build/install/launch, logcat filtering, fixture port-forwarding, instrumented tests, crash/ANR triage. Use when debugging on a real device or emulator.

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

RIPDPI Appium launch-contract reference: start routes and permission/service/data presets. Use when a test launches to the wrong screen or a new automation route is added. General flakiness: appium-test-debug.

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

RIPDPI Appium test authoring: page objects, resource-id locators, assertions, wait tiers. Use when writing a new Appium test or page object, or adding coverage for a new screen.

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

RIPDPI Appium failure triage: flaky tests, locator/session/wait issues, screenshot and element-tree debugging. Use when an Appium test fails or is flaky.

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

Add or modify a GitHub Actions job. Use when editing a file under .github/workflows/. Not for running a workflow locally (local-ci-act) or release signing (release-signing).

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

Conservative review of end-user legal exposure from diagnostics targets and probe hosts, by jurisdiction. Use when shipping or editing diagnostics target lists, probe hosts, or bootstrap endpoints.

日本語の概要は準備中です。原文の説明を表示しています。

po4yka/RIPDPI792026年10月11日 更新

po4yka のスキルをすべて見る

このスキルの問題を報告する