Jetpack Best Practices
coinbase/cds
USE THIS when writing or reviewing Jetpack Compose / Kotlin code in packages/cds-android or apps/android-app - @Composable APIs, Modifier parameters, CompositionLocal, state hoisting, or naming.
ComposeHooks 项目使用指南 - 为 Jetpack Compose/Compose Multiplatform 提供 React Hooks 风格的状态管理和副作用处理。
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install junerver/ComposeHooks compose-hooks --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/compose-hooks .claude/skills/compose-hooks && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .claude/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooksType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install junerver/ComposeHooks compose-hooks --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/compose-hooks .agents/skills/compose-hooks && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .agents/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install junerver/ComposeHooks compose-hooks --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/compose-hooks .cursor/skills/compose-hooks && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .cursor/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/junerver/ComposeHooks.git --path .claude/skills/compose-hooks--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install junerver/ComposeHooks compose-hooks --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/compose-hooks .gemini/skills/compose-hooks && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .gemini/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install junerver/ComposeHooks compose-hooksInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/compose-hooks .github/skills/compose-hooks && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .github/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add junerver/ComposeHooks --skill compose-hooks -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install junerver/ComposeHooks compose-hooks --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/junerver/ComposeHooks.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/compose-hooks .opencode/skills/compose-hooks && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "compose-hooks" agent skill from https://github.com/junerver/ComposeHooks/tree/master/.claude/skills/compose-hooks into .opencode/skills/compose-hooks/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "compose-hooks", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
compose-hooksComposeHooks 项目使用指南 - 为 Jetpack Compose/Compose Multiplatform 提供 React Hooks 风格的状态管理和副作用处理。
Compose Hooks is an agent skill from junerver/ComposeHooks. ComposeHooks 项目使用指南 - 为 Jetpack Compose/Compose Multiplatform 提供 React Hooks 风格的状态管理和副作用处理。 使用此技能当需要:(1) 在 Compose 中管理组件状态 (useState, useGetState, useReducer), (2) 处理副作用和生命周期 (useEffect, useMount, useUnmount), (3) 管理网络请求 (useRequest),(4) 使用防抖/节流 (useDebounce, useThrottle), (5) 管理列表/Map状态 (useList, useMap),(6) 使用定时器 (useInterval, useTimeoutFn/useTimeoutPoll/useCountdown), (7) 全局状态管理 (useSelector, useDispatch),(8) 使用无头表格 (useTable/useTableRequest)。 关键词: Compose, Hooks, 状态管理, useRequest, useReducer, useState…
Its SKILL.md is about 3.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 10 other files, including reference files (for example `references/effect-hooks.md`, `references/form-hooks.md` and `references/platform-hooks.md`).
It sits in Mobile, covering Android development and React components. It works with Jetpack Compose, React and Android. The repository describes itself as: use React style hooks in Compose Multiplatform to easier management state. The licence is Apache-2.0.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 8252192. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are kotlin).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Compose Hooks loads about 3.7k tokens when it runs, and up to ~19k if it reads all its reference files. Until then it costs about 134 tokens; SKILL.md has 860 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from junerver/ComposeHooks at commit 8252192, republished under its Apache-2.0 licence (© junerver). 860 words, ~3,668 tokens.
.claude/skills/compose-hooks/SKILL.md (or your agent's skills folder). This skill also uses 9 other files; get the full folder from GitHub.ComposeHooks 是一个为 Jetpack Compose/Compose Multiplatform 设计的 Hooks 库,灵感来自 React Hooks 和 ahooks。
公开的 useXxx Hook 均提供对应的 rememberXxx 别名,选择你喜欢的命名风格。少数历史 API(如 Table.useTableInstance)仅为兼容保留,优先使用顶层 useXxx/rememberXxx。
库以 Kotlin Multiplatform 形式发布(artifact id hooks2),覆盖四个目标:
| 平台 | target | 说明 |
|---|---|---|
| Android | androidTarget | 含平台专属 hooks(生物识别、网络、电池、传感器等) |
| Desktop | jvm | 桌面专属 useKeyPress |
| iOS | iosArm64 / iosSimulatorArm64 / iosX64 | 状态/副作用/网络等通用 hooks |
| Web | wasmJs (browser) | 自 2.4.0 起可用,用于在浏览器中运行 Compose 组件 |
wasmJs 注意事项:
commonMain 中的 hooks 全部可用;平台强相关 hooks(Android 专属、usePersistent 的存储后端等)需在各 target 的 actual 实现中处理或降级。useRequest/useForm/useRedux 使用的 KClass 仅作 Map 键,wasmJs 下可用;真正的反射调用(.call/.callSuspend/.createType)已隔离在 commonJvmAndroid,不影响 wasmJs 编译。usePersistent 采用注入式设计(SaveToPersistent<T> 函数类型),wasmJs 下可注入基于 localStorage 的实现。| Hook | 用途 | 示例 |
|---|---|---|
useState | 基础状态管理(推荐用 by 委托);派生状态重载 useState(keys) { } 封装 derivedStateOf | var state by useState("") / val full by useState(first, last) { "$first $last" } |
useStateAsync | 异步初始化状态 | val state = useStateAsync { fetchDefault() } |
useGetState | 解构使用的状态管理(推荐) | val (state, setState, getState) = useGetState(0) |
useControllable | 受控/非受控组件 | val (state, setValue) = useControllable(default) |
useResetState | 带重置功能的状态 | val (state, setState, reset) = useResetState("init") |
useBoolean | 布尔状态管理 | val (state, toggle, set, setTrue, setFalse) = useBoolean(false) |
useToggle | 两值切换 | val (state, toggle) = useToggle("A", "B") |
useToggleEither | 不同类型切换 | val (state, toggle) = useToggleEither("left", 100) |
useToggleVisible | 切换内容可见性 | val (content, toggle) = useToggleVisible { Text("Hi") } |
useReducer | Redux 风格状态管理 | val (state, dispatch) = useReducer(reducer, initialState) |
useRef | 不触发重组的引用 | val ref = useRef(0) |
useCreation | 创建复杂对象(类似 useMemo),返回 Ref<T> | val obj by useCreation { ExpensiveObj() } |
usePersistent | 轻量级持久化状态 | val (state, setState) = usePersistent("key", "default") |
usePrevious | 获取前一个值 | val prev = usePrevious(state) |
useLatestRef | 始终返回最新值的引用 | val ref = useLatestRef(state) |
useLatestState | 始终返回最新值的 State<T> | val latest by useLatestState(value) |
useLastChanged | 最后变更时间 | val time = useLastChanged(source) |
useAutoReset | 自动重置状态 | var state by useAutoReset("default", 3.seconds) |
| Hook | 用途 | 示例 |
|---|---|---|
useList | 列表状态管理 | val list = useList(1, 2, 3) |
useListReduce | 列表聚合 | val sum by useListReduce(list) { a, b -> a + b } |
useMap | Map 状态管理 | val map = useMap("key" to "value") |
useImmutableList | 不可变列表 | val (list, mutate) = useImmutableList(1, 2, 3) |
useImmutableListReduce | 不可变列表聚合 | val sum by useImmutableListReduce(list) { a, b -> a + b } |
useSorted | 列表排序 | val sorted by useSorted(list) { a, b -> a.compareTo(b) } |
useCycleList | 循环列表 | val (current, index, next, prev, go) = useCycleList(persistentListOf("A","B","C")) |
useSelectable | 选择/多选 | val (selectedItems, isSelected, toggleSelected) = useSelectable(...) |
| Hook | 用途 | 示例 |
|---|---|---|
useInt | Int 状态 | val count = useInt(0) |
useLong | Long 状态 | val id = useLong(0L) |
useFloat | Float 状态 | val ratio = useFloat(0f) |
useDouble | Double 状态 | val price = useDouble(0.0) |
useCounter | 计数器 | val (count, inc, dec, set, reset) = useCounter(0) |
| Hook | 用途 | 示例 |
|---|---|---|
useEffect | 副作用处理 | useEffect(dep) { /* effect */ } |
useMount | 组件挂载时执行 | useMount { loadData() } |
useUnmount | 组件卸载时执行 | useUnmount { cleanup() } |
useUnmountedRef | 是否已卸载 | val unmounted = useUnmountedRef() |
useUpdateEffect | 跳过首次执行的 Effect | useUpdateEffect(dep) { /* effect */ } |
usePausableEffect | 可暂停/停止的 Effect | val (stop, pause, resume) = usePausableEffect(dep) { } |
useDebounceEffect | 防抖 Effect | useDebounceEffect(dep) { /* 500ms后执行 */ } |
useThrottleEffect | 节流 Effect | useThrottleEffect(dep) { /* 限频执行 */ } |
useBackToFrontEffect | 应用回到前台(Android) | useBackToFrontEffect { refreshData() } |
useFrontToBackEffect | 应用进入后台(Android) | useFrontToBackEffect { saveState() } |
| Hook | 用途 | 示例 |
|---|---|---|
useDebounce | 防抖值 | val debounced = useDebounce(value) |
useDebounceFn | 防抖函数 | val fn = useDebounceFn<String>({ search(it) }) |
useThrottle | 节流值 | val throttled = useThrottle(value) |
useThrottleFn | 节流函数 | val fn = useThrottleFn<Int>({ log(it) }) |
| Hook | 用途 | 示例 |
|---|---|---|
useInterval | 定时器 | useInterval(optionsOf = { period = 1.seconds }) { tick() } |
useTimeout | 延时执行(已废弃,优先用 useTimeoutFn) | useTimeout(3.seconds) { showNotification() } |
useTimeoutFn | 延时执行(可控) | val (pending, start, stop) = useTimeoutFn(fn, 3.seconds) |
useTimeoutPoll | 超时轮询 | useTimeoutPoll({ fetchData() }, 5.seconds) |
useCountdown | 倒计时 | val (remain, formatted) = useCountdown { targetDate = ... } |
| Hook | 用途 | 示例 |
|---|---|---|
useNow | 当前时间(定时更新) | val now = useNow { interval = 1.seconds } |
useTimestamp | 时间戳(定时更新) | val (ts, pause, resume) = useTimestamp { interval = 100.milliseconds } |
useTimestampRef | 时间戳 Ref 版本 | val (ref, pause, resume) = useTimestampRef { interval = 100.milliseconds } |
useDateFormat | 日期格式化 | val formatted = useDateFormat(instant, "YYYY-MM-DD") |
useTimeAgo | 相对时间 | val ago = useTimeAgo(pastInstant) |
| Hook | 用途 | 示例 |
|---|---|---|
useAbs | 绝对值 | val abs = useAbs(value) |
useCeil | 向上取整 | val ceil = useCeil(value) |
useFloor | 向下取整 | val floor = useFloor(value) |
useRound | 四舍五入 | val round = useRound(value) |
useTrunc | 截断 | val trunc = useTrunc(value) |
useMin/useMax | 最小/最大值 | val min = useMin(a, b) |
usePow | 幂运算 | val pow = usePow(base, exp) |
useSqrt | 平方根 | val sqrt = useSqrt(value) |
| Hook | 用途 | 示例 |
|---|---|---|
useAsync | 简化协程 | val run = useAsync { fetchData() } |
useCancelableAsync | 可取消协程 | val (run, cancel, isActive) = useCancelableAsync() |
useMemoizedFn | 记忆化递归函数 | val fn = useMemoizedFn<T, R> { ... } |
| Hook | 用途 | 示例 |
|---|---|---|
useContext | 跨组件共享状态 | val theme = useContext(ThemeContext) |
useEventSubscribe | 事件订阅 | useEventSubscribe<MyEvent> { handle(it) } |
useEventPublish | 事件发布 | val publish = useEventPublish<MyEvent>() |
useUpdate | 强制重组 | val forceUpdate = useUpdate() |
| Hook | 用途 | 示例 |
|---|---|---|
useRequest | 网络请求管理 | val (data, loading, error, request) = useRequest(requestFn) |
useSse | SSE 流式连接 | val (data, streaming, error, _, send, cancel) = useSse(streamFn) |
| Hook | 用途 | 示例 |
|---|---|---|
useSelector | 从 Store 选择状态 | val count by useSelector<AppState, Int> { count } |
useDispatch | 获取 dispatch 函数 | val dispatch = useDispatch<AppAction>() |
useDispatchAsync | 异步 dispatch | val dispatchAsync = useDispatchAsync<AppAction>() |
useStateMachine | 状态机 | val (state, send) = useStateMachine(graph) |
| Hook | 用途 | 示例 |
|---|---|---|
useUndo | 撤销/重做 | val (state, set, reset, undo, redo) = useUndo(initial) |
| Hook | 用途 | 示例 |
|---|---|---|
useClipboard | 剪贴板操作 | val (copy, paste) = useClipboard() |
useKeyboard | 软键盘显隐控制 | val (hideKeyboard, showKeyboard) = useKeyboard() |
| Hook | 用途 | 示例 |
|---|---|---|
Form.useForm | 表单实例 | val form = Form.useForm() |
Form.useWatch | 监听表单值 | val value = Form.useWatch("field", form) |
Form.useFormInstance | 获取表单实例 | val form = Form.useFormInstance() |
| Hook | 用途 | 示例 |
|---|---|---|
useTable | 无头表格 | val table = useTable(data, columns) { ... } |
useTableRequest | 分页请求表格 | val tableReq = useTableRequest(requestFn) |
| Hook | 用途 | 示例 |
|---|---|---|
useBiometric | 生物识别 | val (openBiometric, isAuthed) = useBiometric() |
useNetwork | 网络状态 | val network by useNetwork() |
useBatteryInfo | 电池信息 | val battery by useBatteryInfo() |
useBuildInfo | 设备信息 | val build = useBuildInfo() |
useScreenInfo | 屏幕信息 | val screen = useScreenInfo() |
useVibrate | 振动 | val (shortVibrate, longVibrate) = useVibrate() |
useFlashlight | 手电筒 | val (turnOn, turnOff) = useFlashlight() |
useWakeLock | 唤醒锁 | val (request, release, isActive) = useWakeLock() |
useSensor | 传感器 | useSensor(Sensor.TYPE_ACCELEROMETER) { event -> ... } |
useIlluminance | 光照强度 | val illuminance = useIlluminance() |
useIdle | 空闲检测 | val idle = useIdle() |
useScreenBrightness | 屏幕亮度 | val (setBrightness, initialBrightness) = useScreenBrightness() |
useDisableScreenshot | 禁用截图 | val (disable, enable, isDisabled) = useDisableScreenshot() |
useWindowFlags | 窗口标志 | val (add, clear, isAdded) = useWindowFlags(key, flags) |
| Hook | 用途 | 示例 |
|---|---|---|
useKeyPress | 键盘按键检测 | useKeyPress(Key.Enter) { handleEnter() } |
// 推荐:使用 useGetState 解构
val (text, setText) = useGetState("")
OutlinedTextField(
value = text.value,
onValueChange = setText,
label = { Text("输入") }
)
// 或使用 by 委托
var text by useState("")
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("输入") }
)// 方式1: 使用 useGetState 函数式更新
val (state, setState) = useGetState("initial")
LaunchedEffect(Unit) {
repeat(10) {
delay(1.seconds)
setState { "$it." } // 函数式更新,避免闭包问题
}
}
// 方式2: 使用 by 委托
var byState by useState("initial")
LaunchedEffect(Unit) {
repeat(10) {
delay(1.seconds)
byState += "." // 直接修改,无闭包问题
}
}
// 方式3: 使用 useLatestRef
val (state, setState) = useState("initial")
val stateRef = useLatestRef(state)
LaunchedEffect(Unit) {
repeat(10) {
delay(1.seconds)
setState("${stateRef.current}.") // 通过 ref 获取最新值
}
}// 自动请求
val (userInfoState, loadingState, errorState) = useRequest(
requestFn = { NetApi.userInfo(it) },
optionsOf = {
defaultParams = "junerver" // 自动请求必须设置默认参数
}
)
val userInfo by userInfoState
val loading by loadingState
if (loading) {
Text(text = "loading ...")
}
userInfo?.let { Text(text = it.toString()) }
// 手动请求
val (repoInfoState, loadingState, errorState, request) = useRequest(
requestFn = { it: Tuple2<String, String> ->
NetApi.repoInfo(it.first, it.second)
},
optionsOf = {
manual = true
defaultParams = tuple("junerver", "ComposeHooks")
}
)
TButton(text = "request") { request() }// 定义 State 和 Action
data class SimpleData(val name: String, val age: Int)
sealed interface SimpleAction {
data class ChangeName(val newName: String) : SimpleAction
data object AgeIncrease : SimpleAction
}
// 定义 Reducer
val simpleReducer: Reducer<SimpleData, SimpleAction> = { prevState, action ->
when (action) {
is SimpleAction.ChangeName -> prevState.copy(name = action.newName)
is SimpleAction.AgeIncrease -> prevState.copy(age = prevState.age + 1)
}
}
// 使用
val (state, dispatch) = useReducer(
simpleReducer,
initialState = SimpleData("default", 18),
middlewares = arrayOf(logMiddleware())
)
TButton(text = "Change Name") { dispatch(SimpleAction.ChangeName("Alice")) }
TButton(text = "Increase Age") { dispatch(SimpleAction.AgeIncrease) }
Text(text = "State: ${state.value}")val listState = useList(1, 2, 3)
// 操作方法
listState.add(4) // 添加
listState.add(0, 0) // 插入
listState.removeAt(0) // 删除
listState.removeLast() // 删除最后一个
listState[0] = 10 // 修改
listState.clear() // 清空
listState.shuffle() // 打乱
// 配合 useListReduce
val sum by useListReduce(listState) { a, b -> a + b }var inputValue by useState("")
val debouncedValue by useDebounce(
value = inputValue,
optionsOf = {
wait = 500.milliseconds
}
)
OutlinedTextField(
value = inputValue,
onValueChange = { inputValue = it },
label = { Text("Type something...") }
)
Text(text = "Debounced: $debouncedValue")// 自动连接
val (lastEvent, isStreaming, error) = useSse(
streamFn = { params: String -> sseService.subscribe(params) },
optionsOf = {
defaultParams = "topic-1"
onEvent = { event -> println("收到: $event") }
}
)
// 手动连接
val (lastEvent, isStreaming, error, params, send, cancel) = useSse(
streamFn = { url: String -> sseClient.connect(url) },
optionsOf = { manual = true }
)
Button(onClick = { send("https://api.example.com/events") }) {
Text("开始监听")
}© junerver, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 9 other files (references) in .claude/skills/compose-hooks of junerver/ComposeHooks.
Open the folder on GitHubat commit 8252192
Compose Hooks next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Compose Hooks this skilljunerver/ComposeHooks | 105 | — | ~3.7k | Automated safety check: Pass | Apache-2.0 | |
| Jetpack Best Practicescoinbase/cds | 506 | — | ~805 | Automated safety check: Pass | Custom licence | |
| Compose Multiplatform Patternsmonta-app/ocpp-emulator | 179 | 5 repos | ~2k | Automated safety check: Pass | Apache-2.0 | |
| Android Developmentdpconde/claude-android-skill | 336 | — | ~1.7k | Automated safety check: Pass | MIT | |
| Stylesarindamxd/camerax-android | 132 | 4 repos | ~2.3k | Automated safety check: Pass | Apache-2.0 | |
| Benchmarkandroidx/androidx | 6.1k | — | ~1.1k | Automated safety check: Pass | Apache-2.0 |
coinbase/cds
USE THIS when writing or reviewing Jetpack Compose / Kotlin code in packages/cds-android or apps/android-app - @Composable APIs, Modifier parameters, CompositionLocal, state hoisting, or naming.
monta-app/ocpp-emulator
Compose Multiplatform and Jetpack Compose patterns for KMP projects — state management, navigation, theming, performance, and platform-specific UI.
dpconde/claude-android-skill
Create production-quality Android applications following Google's official architecture guidance and NowInAndroid best practices.
arindamxd/camerax-android
A skill your agent uses to integrate the Jetpack Compose Styles API into an Android project.
androidx/androidx
Benchmarking and improving the performance of Jetpack Compose.
openvetta/open-vetta
Master Material Design 3 and Jetpack Compose patterns for building native Android apps.
Works with
Categories
ComposeHooks 项目使用指南 - 为 Jetpack Compose/Compose Multiplatform 提供 React Hooks 风格的状态管理和副作用处理。. Compose Hooks is an agent skill from junerver/ComposeHooks.
Compose Hooks fits situations like: tasks that involve Android development; tasks that involve React components.
Run `npx skills add junerver/ComposeHooks --skill compose-hooks -a claude-code`. Or copy the skill folder (.claude/skills/compose-hooks in junerver/ComposeHooks) into .claude/skills/compose-hooks in your project. Claude Code loads it when a task matches its description.
Run `npx skills add junerver/ComposeHooks --skill compose-hooks -a codex`. Or copy the skill folder (.claude/skills/compose-hooks in junerver/ComposeHooks) into .agents/skills/compose-hooks in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add junerver/ComposeHooks --skill compose-hooks -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/compose-hooks, .gemini/skills/compose-hooks, .github/skills/compose-hooks and .opencode/skills/compose-hooks in your project.
SKILL.md names no scripts, command-line tools or credentials: Compose Hooks is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Compose Hooks is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3.7k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 15k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Compose Hooks: Jetpack Best Practices (coinbase/cds, 506 stars), Compose Multiplatform Patterns (monta-app/ocpp-emulator, 179 stars), Android Development (dpconde/claude-android-skill, 336 stars) and Styles (arindamxd/camerax-android, 132 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
junerver (a GitHub user) maintains it in junerver/ComposeHooks, which has 105 GitHub stars. The repository was last updated on July 17, 2026.
Source: junerver/ComposeHooks on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.