Token导航 LogoToken导航TokenDH.com
前端设计需要联网github未标认证来源可访问许可证需确认审计通过

android-compose安卓撰写

Agent Skill

android-compose 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

218

周安装

9

GitHub Stars

8

下载量

71
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:android-compose(安卓撰写)
来源仓库:https://github.com/ar4mirez/samuel
仓库路径:skills/android-compose
安装命令:
npx skills add https://github.com/ar4mirez/samuel --skill android-compose
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/ar4mirez/samuel --skill android-compose

简介

用于 Jetpack Compose 声明式 UI 开发的完整指南与规范参考。

  • 涵盖单向数据流、状态提升、生命周期感知与 Material Design 3 适配。
  • 提供可组合函数编写规范与性能优化 guardrails 清单。
  • 适用于构建响应式、可维护且高性能的 Android 用户界面。
  • 安装前请确保项目满足最低 SDK 与 Kotlin 版本要求。

SKILL.md

Android Jetpack Compose Guide

Applies to: Android SDK 24+, Jetpack Compose 1.5+, Kotlin 1.9+, Material Design 3

Core Principles

  1. Declarative UI: Describe what the UI should look like, not how to build it step-by-step
  2. Unidirectional Data Flow: State flows down, events flow up -- always
  3. Composable Functions Are Cheap: The framework handles when to recompose; keep composables pure
  4. State Hoisting: Lift state to the nearest common ancestor that needs it
  5. Lifecycle Awareness: Collect flows with collectAsStateWithLifecycle(), never raw collectAsState()

Guardrails

Composable Conventions

  • Every composable accepts modifier: Modifier = Modifier as its first optional parameter
  • Keep composables small and focused (one responsibility per function)
  • Use @Preview on every reusable component with representative data
  • Never perform side effects (network, DB, analytics) directly inside a composable body
  • Use LaunchedEffect, DisposableEffect, or SideEffect for side-effect work
  • Prefer stateless composables; hoist state to the caller or ViewModel
// BAD: side effects in composition
@Composable
fun UserList(users: List<User>) {
    analytics.trackScreenView("user_list") // fires on every recomposition
    LazyColumn { ... }
}

// GOOD: side effect in LaunchedEffect
@Composable
fun UserList(users: List<User>) {
    LaunchedEffect(Unit) {
        analytics.trackScreenView("user_list")
    }
    LazyColumn { ... }
}

State Management

  • Use StateFlow in ViewModel, collect with collectAsStateWithLifecycle()
  • Model UI state as a sealed interface (Loading, Success, Error)
  • Never expose MutableStateFlow publicly from a ViewModel
  • Use remember for local composable state; rememberSaveable when it must survive config changes
  • Use derivedStateOf for expensive computations that depend on other state
// Sealed UI state pattern
sealed interface HomeUiState {
    data object Loading : HomeUiState
    data class Success(val users: List<User>) : HomeUiState
    data class Error(val message: String) : HomeUiState
}

// ViewModel exposes immutable StateFlow
@HiltViewModel
class HomeViewModel @Inject constructor(
    private val userRepository: UserRepository,
) : ViewModel() {
    private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()

    fun loadUsers() {
        viewModelScope.launch {
            _uiState.value = HomeUiState.Loading
            userRepository.getUsers()
                .catch { e -> _uiState.value = HomeUiState.Error(e.message ?: "Unknown error") }
                .collect { users -> _uiState.value = HomeUiState.Success(users) }
        }
    }
}

// Screen collects lifecycle-aware
@Composable
fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    when (val state = uiState) {
        is HomeUiState.Loading -> LoadingIndicator()
        is HomeUiState.Success -> UserList(state.users)
        is HomeUiState.Error -> ErrorMessage(state.message, onRetry = viewModel::loadUsers)
    }
}

Navigation

  • Define routes as a sealed class or sealed interface for type safety
  • Never pass complex objects as navigation arguments; pass IDs and load from ViewModel
  • Clear back stack correctly with popUpTo and inclusive when navigating to auth screens
  • Use hiltViewModel() inside composable {} blocks for scoped ViewModels
sealed class Screen(val route: String) {
    data object Login : Screen("login")
    data object Home : Screen("home")
    data object Detail : Screen("detail/{userId}") {
        fun createRoute(userId: String) = "detail/$userId"
    }
}

@Composable
fun AppNavigation() {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = Screen.Home.route) {
        composable(Screen.Home.route) {
            HomeScreen(onUserClick = { userId ->
                navController.navigate(Screen.Detail.createRoute(userId))
            })
        }
        composable(
            route = Screen.Detail.route,
            arguments = listOf(navArgument("userId") { type = NavType.StringType }),
        ) { backStackEntry ->
            val userId = backStackEntry.arguments?.getString("userId") ?: ""
            DetailScreen(userId = userId, onBack = { navController.popBackStack() })
        }
    }
}

Material 3 Theming

  • Always wrap the app root in your custom MaterialTheme composable
  • Support dynamic color on Android 12+ with dynamicLightColorScheme / dynamicDarkColorScheme
  • Define color tokens in a dedicated Color.kt; typography in Type.kt; theme in Theme.kt
  • Use MaterialTheme.colorScheme.* and MaterialTheme.typography.* instead of hardcoded values
  • Support both light and dark themes from day one
@Composable
fun MyAppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit,
) {
    val colorScheme = when {
        dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
            val context = LocalContext.current
            if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
        }
        darkTheme -> DarkColorScheme
        else -> LightColorScheme
    }
    MaterialTheme(colorScheme = colorScheme, typography = Typography, content = content)
}

Dependency Injection (Hilt)

  • Annotate Application with @HiltAndroidApp, Activity with @AndroidEntryPoint
  • Annotate ViewModels with @HiltViewModel and use @Inject constructor
  • Use hiltViewModel() in composables, never construct ViewModels manually
  • Organize DI modules by layer: AppModule, NetworkModule, DatabaseModule

Lifecycle & Effects

EffectTriggerUse for
LaunchedEffect(key)On enter + when key changesOne-shot loads, navigation events
DisposableEffect(key)On enter + when key changes (with cleanup)Listeners, observers, callbacks
SideEffectAfter every successful recompositionSyncing compose state to non-compose
rememberCoroutineScope()Manual launch from callbacksButton clicks launching coroutines
// One-shot load on screen enter
LaunchedEffect(Unit) { viewModel.loadData() }

// Clean up a listener when leaving composition
DisposableEffect(lifecycleOwner) {
    val observer = LifecycleEventObserver { _, event -> ... }
    lifecycleOwner.lifecycle.addObserver(observer)
    onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}

Project Structure

app/src/main/java/com/example/myapp/
├── MyApplication.kt               # @HiltAndroidApp
├── MainActivity.kt                # @AndroidEntryPoint, setContent {}
├── navigation/
│   └── AppNavigation.kt           # NavHost, route definitions
├── ui/
│   ├── theme/                     # Color.kt, Type.kt, Theme.kt
│   ├── screens/                   # Feature screens
│   │   ├── home/
│   │   │   ├── HomeScreen.kt      # Composable
│   │   │   └── HomeViewModel.kt   # ViewModel + UiState
│   │   └── detail/
│   │       ├── DetailScreen.kt
│   │       └── DetailViewModel.kt
│   └── components/                # Shared composables
│       ├── LoadingIndicator.kt
│       └── ErrorMessage.kt
├── data/
│   ├── model/                     # Domain models (@Serializable)
│   ├── remote/                    # Retrofit service, DTOs
│   ├── local/                     # Room database, DAOs
│   └── repository/                # Repository implementations
└── di/                            # Hilt modules
    ├── AppModule.kt
    └── NetworkModule.kt
  • ui/screens/: One sub-package per feature, each with Screen.kt + ViewModel.kt
  • ui/components/: Reusable composables shared across screens
  • data/: Models, repositories, API services; no Compose dependencies here
  • di/: Hilt modules providing singletons and scoped dependencies

Key Dependencies

// build.gradle.kts (app)
val composeBom = platform("androidx.compose:compose-bom:2024.01.00")
implementation(composeBom)
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.activity:activity-compose:1.8.2")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
implementation("androidx.navigation:navigation-compose:2.7.6")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
implementation("com.google.dagger:hilt-android:2.50")
ksp("com.google.dagger:hilt-compiler:2.50")
implementation("io.coil-kt:coil-compose:2.5.0")

// Testing
testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.13.9")
testImplementation("app.cash.turbine:turbine:1.0.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
androidTestImplementation(composeBom)
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")

Testing

ViewModel Unit Tests

  • Use StandardTestDispatcher + Dispatchers.setMain() for coroutine control
  • Use Turbine for StateFlow assertions
  • Use MockK with coEvery / coVerify for suspend function mocking
  • Reset dispatcher in @After with Dispatchers.resetMain()
@OptIn(ExperimentalCoroutinesApi::class)
class HomeViewModelTest {
    private val testDispatcher = StandardTestDispatcher()
    private val userRepository = mockk<UserRepository>()

    @Before fun setup() { Dispatchers.setMain(testDispatcher) }
    @After fun tearDown() { Dispatchers.resetMain() }

    @Test
    fun `loadUsers emits Success state on valid data`() = runTest {
        val users = listOf(User(id = "1", email = "a@b.com", name = "Alice"))
        coEvery { userRepository.getUsers() } returns flowOf(users)

        val viewModel = HomeViewModel(userRepository)
        advanceUntilIdle()

        viewModel.uiState.test {
            val state = awaitItem()
            assertTrue(state is HomeUiState.Success)
            assertEquals(users, (state as HomeUiState.Success).users)
        }
    }
}

Compose UI Tests

  • Use createComposeRule() for isolated composable tests
  • Query nodes with onNodeWithText, onNodeWithTag, onNodeWithContentDescription
  • Assert with assertExists(), assertIsDisplayed(), assertIsEnabled()
  • Perform actions with performClick(), performTextInput()
class HomeScreenTest {
    @get:Rule val composeTestRule = createComposeRule()

    @Test
    fun displays_user_name_when_loaded() {
        composeTestRule.setContent {
            MyAppTheme {
                UserCard(user = User("1", "a@b.com", "Alice"), onClick = {}, onDeleteClick = {})
            }
        }
        composeTestRule.onNodeWithText("Alice").assertIsDisplayed()
        composeTestRule.onNodeWithText("a@b.com").assertIsDisplayed()
    }
}

Commands

# Build
./gradlew assembleDebug            # Debug APK
./gradlew assembleRelease          # Release APK
./gradlew bundleRelease            # AAB for Play Store

# Test
./gradlew test                     # Unit tests
./gradlew connectedAndroidTest     # Instrumented tests on device/emulator

# Quality
./gradlew lint                     # Android Lint
./gradlew ktlintCheck              # Code style check
./gradlew ktlintFormat             # Auto-fix formatting
./gradlew detekt                   # Static analysis

# Install & Run
./gradlew installDebug             # Install on connected device
./gradlew clean                    # Clean build artifacts

Best Practices Summary

Do

  • Use collectAsStateWithLifecycle() for all Flow collection in composables
  • Use remember {} for expensive local computations
  • Use LaunchedEffect for one-shot side effects, DisposableEffect for cleanup
  • Keep composables stateless; hoist state to ViewModel or caller
  • Use sealed interfaces for UI state with exhaustive when expressions
  • Provide contentDescription on all interactive and decorative icons
  • Use Modifier chaining; accept modifier parameter in every public composable
  • Preview every reusable component with @Preview

Avoid

  • Performing I/O, network calls, or analytics tracking directly in composable bodies
  • Exposing MutableStateFlow or MutableState from ViewModels
  • Using !! operator in production code
  • Hardcoding colors, dimensions, or strings (use theme tokens and resources)
  • Creating ViewModels manually (use hiltViewModel())
  • Passing complex objects through navigation arguments (pass IDs instead)
  • Ignoring configuration changes (use rememberSaveable when needed)

References

For detailed UI patterns, animation, testing strategies, performance, and accessibility guidance, see:

External References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

37.41%
按下载量换算27

Claude

31.3%
按下载量换算22

Cursor

16.72%
按下载量换算12

Gemini CLI

9.02%
按下载量换算6

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills