원클릭으로
android-compose-patterns
Compose UI, ViewModels, navigation, state, and app-module theming.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Compose UI, ViewModels, navigation, state, and app-module theming.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Use this skill to generate well-branded interfaces and assets for RIPDPI (an Android-native, Compose-first VPN and DPI-bypass app), either for production or throwaway prototypes/mocks/etc. Contains essential design guidelines, colors, type, fonts, brand-ship icons, and an interactive UI kit of every public composable from the live ui/components tree.
Use when managing the Rust workspace, adding/removing crates, editing workspace dependencies, running cargo nextest/audit/deny, configuring Cargo profiles for Android cross-compilation, debugging Cargo.lock churn, migrating crate edition, or wiring Gradle to cargo via the ripdpi.android.rust-native plugin.
Use when modifying the diagnostics scan pipeline, ScanRequest/ScanReport types, ProbeTask families, ripdpi-monitor-engine / ripdpi-diagnostics-* crates, strategy-probe candidates, the diagnostics catalog (packs/profiles), wire-schema contracts between Rust and Kotlin, DIAGNOSTICS_ENGINE_SCHEMA_VERSION, golden contract tests, or adding a new probe type / profile. Triggers on diagnostics scans, strategy probes, automatic audit, dpi-detector profiles, or anything in core/diagnostics or native/rust/crates/ripdpi-monitor-*.
Android-specific Rust build, verification, and packaging — per-target 16 KiB page alignment, size-optimized release profile, ELF symbol allowlist, .so size budgets, NDK 29 specifics. Use when modifying .cargo/config.toml for Android targets, the workspace [profile.release] / [profile.android-jni] block, or when verifying a built .so before release.
Telemetry and observability discipline for the RIPDPI Android Rust stack — control-plane vs data-plane logging, bounded flume event queues, atomic counters, snapshot polling, readiness callbacks, and deterministic JSON contracts. Use when authoring or modifying telemetry emission code, the bounded event ring, the Kotlin-side telemetry consumer, or any per-packet logging.
Custom detekt rules, DI/privacy/suppression guardrails, detekt.yml configuration, and false-positive triage.
| name | android-compose-patterns |
| description | Compose UI, ViewModels, navigation, state, and app-module theming. |
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.
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
Routes are defined as a sealed class in app/.../ui/navigation/Route.kt.
Route sealed class with route string, @StringRes titleRes, optional iconRoute.topLevel listRoute.all listcomposable(Route.YourRoute.route) { ... } in RipDpiNavHost.ktnavController.navigate(Route.YourRoute.route) { launchSingleTop = true; restoreState = true }launchSingleTop = true and restoreState = true for all navigationsisTopLevelDestination() routesclass 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 { /* ... */ } }
}
SharingStarted.WhileSubscribed(5_000) for all StateFlow exportsAndroidViewModel (not plain ViewModel) when DataStore access neededMutex for thread-safe state transitions (see MainViewModel.toggleService)Channel<Effect> for one-shot UI events, collected via LaunchedEffect// 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) { /* ... */ }
collectAsStateWithLifecycle() (not collectAsState())RipDpiThemeTokens for colors, spacing, typography| ViewModel | Location | Purpose |
|---|---|---|
MainViewModel | activities/MainViewModel.kt | Connection state, VPN/proxy toggle, metrics |
ConfigViewModel | activities/ConfigViewModel.kt | Proxy config presets, draft editing, validation |
SettingsViewModel | activities/SettingsViewModel.kt | App settings, theme, DataStore persistence |
DiagnosticsViewModel | activities/DiagnosticsViewModel.kt | Diagnostics scan orchestration, history, export/share state, and strategy-probe presentation |
activities/DiagnosticsUi* support files instead of recomputing report metadata directly in composables.app/src/main/kotlin/com/poyka/ripdpi/ui/testing/RipDpiTestTags.kt for any new externally exercised UI.onOpenAdvancedSettings, onSelectCandidate, and sheet-dismiss actions through Route/Screen parameters rather than letting deep child composables navigate directly.| Mistake | Fix |
|---|---|
Using collectAsState() | Use collectAsStateWithLifecycle() for lifecycle awareness |
| Passing ViewModel to Screen composable | Pass uiState and callbacks; keep Screen stateless |
| Hardcoding colors/spacing | Use RipDpiThemeTokens and Material 3 theme |
Missing WhileSubscribed(5_000) | Required for proper lifecycle handling in all StateFlow exports |
| Creating ViewModel in Composable | Use viewModel() in Route composable or pass from NavHost |
jetpack-compose-api (.github/skills/jetpack-compose-api/SKILL.md):
General Jetpack Compose API reference with guidance docs and actual androidx source code.
Use for questions about how Compose APIs work internally, correct API usage patterns,
recomposition mechanics, Modifier ordering, side-effects, performance, or accessibility.