一键导入
overdraw-and-layout
Reduce overdraw, layout depth, and unnecessary recomposition/re-render in native Android, iOS, Compose, SwiftUI, Flutter, and React Native UIs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Reduce overdraw, layout depth, and unnecessary recomposition/re-render in native Android, iOS, Compose, SwiftUI, Flutter, and React Native UIs.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Schedule, batch, and constrain background work with WorkManager, BGTaskScheduler, and push-over-poll patterns to minimize battery and data cost.
Attribute battery drain to CPU, radio, GPS, display, and background work using Android Battery Historian and Xcode Energy Log / Instruments Energy gauge.
Shrink APK/AAB and IPA size with R8/ProGuard, dead-code elimination, asset hygiene, and Android App Bundle / App Thinning best practices.
Cut local and CI build times on Android (Gradle config cache, build cache, KSP over KAPT) and iOS (Xcode build cache, ccache, modules). Use when clean builds exceed ten minutes.
Downsample, cache, and choose the right image format (AVIF, HEIC, WebP) to cut memory, network, and decode time on iOS and Android.
Find and fix common memory leaks caused by listeners, closures, singletons, and lifecycle mismatches on iOS, Android, Flutter, and React Native.
| name | overdraw-and-layout |
| description | Reduce overdraw, layout depth, and unnecessary recomposition/re-render in native Android, iOS, Compose, SwiftUI, Flutter, and React Native UIs. |
Overdraw means the GPU painted the same pixel multiple times. Deep layout hierarchies mean the CPU measured and positioned views the user never sees. Both waste the frame budget. This skill covers how to see the problem and how to fix it.
Android: Developer Options → Debug GPU overdraw → Show overdraw areas. Blue = 1x (fine), green = 2x, pink = 3x, red = 4x+. Red areas are the target.
iOS: Xcode → Debug → View Debugging → Rendering → Color Blended Layers. Green layers are opaque (good), red are blended (expensive). Also Color Misaligned Images and Color Off-screen Rendered.
Flutter: in main:
debugPaintLayerBordersEnabled = true; // shows layer boundaries
debugRepaintRainbowEnabled = true; // rainbow cycles on every repaint
React Native: with Flipper → Layout Inspector, enable Show Overdraw on Android device.
isOpaque = true on views whose background is fully opaque.android:background from intermediate ViewGroups; let the window background show through.Container(decoration: BoxDecoration(color: ...)) for no reason.A deep hierarchy costs measure + layout passes on every frame.
Android View system:
ConstraintLayout over nested LinearLayout/RelativeLayout.ScrollView > LinearLayout > N children with RecyclerView.<merge> in reusable layouts to collapse a pointless root.Compose:
Box inside Box inside Box. Use Modifier.padding/background/clickable instead of wrapper Box.LazyColumn/LazyRow over Column { items.forEach { ... } }.SwiftUI:
LazyVStack instead of VStack for large lists.Group { Group { ... } } wrappers; they do not add value.AnyView — it erases types and blocks SwiftUI's diffing.Flutter:
Column(children: [Padding(Padding(Padding(...)))]) with a single Padding + direct child.Flex/Spacer/Expanded instead of multiple SizedBox.RepaintBoundary around parts that repaint independently (e.g., a ticking clock above a static feed).React Native:
<View> layers. Each native View adds a shadow node and a native view.collapsable={true} hints and Fabric's view flattening.flex: 1 everywhere can force extra layout passes; set explicit dimensions when known.Compose — only recompose what changed:
@Composable
fun Counter(count: Int) {
// Do NOT compute derived values with Modifier.drawBehind using the raw state
val label = remember(count) { "Count: $count" }
Text(label)
}
@Composable
fun Screen(state: ScreenState) {
// Use derivedStateOf to avoid recomposing on unrelated state fields
val canSubmit by remember(state) { derivedStateOf { state.name.isNotBlank() } }
Button(enabled = canSubmit, onClick = state.onSubmit) { Text("Submit") }
}
Mark classes @Stable / @Immutable when their equality is value-based so Compose can skip recomposition.
SwiftUI — extract subviews so @State changes do not invalidate siblings. Use EquatableView for heavy leaf views.
React (RN) — React.memo rows; keep onPress identity stable with useCallback:
const Row = React.memo(function Row({ item, onPress }: Props) {
return <Pressable onPress={() => onPress(item.id)}><Text>{item.title}</Text></Pressable>;
});
Flutter — prefer const constructors everywhere possible. Use Selector (provider) or select (Riverpod) to subscribe to a slice of state instead of the whole object.
RecyclerView with DiffUtil or ListAdapter. Set setHasFixedSize(true) if bounds do not change with content.LazyColumn with stable key = { it.id } and contentType when heterogeneous.List or LazyVStack(pinnedViews:). Provide id: explicitly; avoid UUID() per render.ListView.builder with itemExtent/prototypeItem for uniform rows.FlatList with keyExtractor, getItemLayout, removeClippedSubviews={true}, or migrate to FlashList.derivedStateOf for derived values, stable key/contentType in lazy lists.AnyView on hot paths; large leaves are equatable.const constructors; RepaintBoundary around independently repainting subtrees.FlatList/FlashList configured with keyExtractor and getItemLayout.