Edge To Edge

作者 android42dc2270e960Complete terms in LICENSE.txt7.7K 個星標收錄於 2026年10月8日更新於 2026年10月8日儲存庫13 天前更新

Use this skill to migrate your Jetpack Compose app to add adaptive edge-to-edge support and troubleshoot common issues. Use this skill to fix UI components (like buttons or lists) that are obscured by or overlapping with the navigation bar or status bar, fix IME insets, and fix system bar legibility.

AI 產生的概覽

指導將 Android Jetpack Compose 應用程式遷移為自適應邊到邊顯示,並修正系統列、輸入法與內距問題。

功能
提供為 Android Jetpack Compose 應用程式加入邊到邊支援的逐步流程:依 Activity 規劃變更、呼叫 enableEdgeToEdge、設定 adjustResize,並套用視窗內距。內容涵蓋 Scaffold 與 Material 元件的內距處理、自適應 Scaffold、附正確與錯誤程式碼範例的輸入法內距模式、導覽列對比度與系統列圖示可讀性、清單以及全螢幕對話方塊。最後提供檢查清單,包括執行 Gradle 建置。
適用情境
適用於將 Jetpack Compose 應用程式遷移到邊到邊顯示,或按鈕、清單、文字輸入框等介面元件被導覽列、狀態列或輸入法遮住、重疊的情況。也適用於系統列圖示在應用程式內容上難以辨識時。
執行需求
一個使用 Jetpack Compose 且目標 SDK 為 35 或以上的 Android 專案;不隨附指令碼,僅為說明文件。檢查清單提到執行 ./gradlew build 進行驗證。

Prerequisites

  • Project MUST use Android Jetpack Compose.
  • Project MUST target SDK 35 or later. If the SDK is lower than 35, increase the SDK to 35.

Step 1: plan

  1. Locate and analyze all Activity classes to detect which have existing edge-to-edge support. For every Activity without edge-to-edge, plan to make each Activity edge-to-edge.
  2. In each Activity, Locate and analyze all lists and FAB components to detect which have existing edge-to-edge support. For every component without edge-to-edge support, plan to make each of these components edge-to-edge.
  3. In each Activity, scan for TextField, OutlinedTextField, or BasicTextField. If found, then you MUST verify the IME doesn't hide the input field by following the IME section of this skill.

Step 2: add edge-to-edge support

  1. Add enableEdgeToEdge before setContent in onCreate in each Activity that does not already call enableEdgeToEdge.
  2. Add android:windowSoftInputMode="adjustResize" in the AndroidManifest.xml for all Activities that use a soft keyboard.

Step 3: apply insets

  • The app MUST apply system insets, or align content to rulers, so critical UI remains tappable. Choose only one method to avoid double padding:

    1. PREFERRED: When available, use Scaffolds and pass PaddingValues to the content lambda.
    kotlin
    Scaffold { innerPadding ->    // innerPadding accounts for system bars and any Scaffold components    LazyColumn(        modifier = Modifier            .fillMaxSize()            .consumeWindowInsets(innerPadding),        contentPadding = innerPadding    ) { /* Content */ }}
    <br />
    1. PREFERRED: When available, use the automatic inset handling or padding modifiers in material components.

      • Material 3 Components manages safe areas for its own components, including:
        • TopAppBar
        • SmallTopAppBar
        • CenterAlignedTopAppBar
        • MediumTopAppBar
        • LargeTopAppBar
        • BottomAppBar
        • ModalDrawerSheet
        • DismissibleDrawerSheet
        • PermanentDrawerSheet
        • ModalBottomSheet
        • NavigationBar
        • NavigationRail
      • For Material 2 Components, use the windowInsetsparameter to apply insets manually for BottomAppBar, TopAppBar and BottomNavigation. DO NOT apply padding to the parent container; instead, pass insets directly to the App Bar component. Applying padding to the parent container prevents the App Bar background from drawing into the system bar area. For example, for TopAppBar, choose only one of the following options:
        1. PREFERRED: TopAppBar(windowInsets = AppBarDefaults.topAppBarWindowInsets)
        2. TopAppBar(windowInsets = WindowInsets.systemBars.exclude(WindowInsets.navigationBars))
        3. TopAppBar(windowInsets = WindowInsets.systemBars.add(WindowInsets.captionBar))
    2. For components outside a Scaffold, use padding modifiers, such as Modifier.safeDrawingPadding() or Modifier.windowInsetsPadding(WindowInsets.safeDrawing).

      kotlin
      Box(    modifier = Modifier        .fillMaxSize()        .safeDrawingPadding()) {    Button(        onClick = {},        modifier = Modifier.align(Alignment.BottomCenter)    ) {        Text("Login")    }}
      <br />
    3. For deeply nested components with excessive padding, use WindowInsetsRulers (e.g. Modifier.fitInside(WindowInsetsRulers.SafeDrawing.current)). See the IME section for a code sample.

    4. When you need an element (e.g. a custom header or decorative scrim) to equal the dimensions of a system bar, use inset size modifiers (e.g. Modifier.windowInsetsTopHeight(WindowInsets.systemBars)). See the Lists section for a code sample.

Adaptive Scaffolds

  • NavigationSuiteScaffold manages safe areas for its own components, like the NavigationRail or NavigationBar. However, the adaptive scaffolds (e.g. NavigationSuiteScaffold, ListDetailPaneScaffold) don't propagate PaddingValues to their inner contents. You MUST apply insets to individual screens or components (e.g., list contentPadding or FAB padding) as described in Step 3 . DO NOT apply safeDrawingPadding or similar modifiers to the NavigationSuiteScaffold parent. This clips and prevents an edge-to-edge screen.

IME

  • For each Activity with a soft keyboard, check that android:windowSoftInputMode="adjustResize" is set in the AndroidManifest.xml. DO NOT use SOFT_INPUT_ADJUST_RESIZE because it is deprecated. Then, maintain focus on the input field. Choose one:
      1. PREFERRED: Add Modifier.fitInside(WindowInsetsRulers.Ime.current) to the content container. This is preferred over imePadding() because it reduces jank and extra padding caused by forgetting to consume insets upstream in the hierarchy.
      1. Add imePadding to the content container. The padding modifier MUST be placed before Modifier.verticalScroll(). Do NOT use Modifier.imePadding() if the parent already accounts for the IME with contentWindowInsets (e.g. contentWindowInsets = WindowInsets.safeDrawing). Doing so will cause double padding.

IMEs with Scaffolds code patterns

RIGHT

RIGHT because contentWindowInsets contains IME insets, which are passed to the content lambda as innerPadding.

kotlin
// RIGHTScaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->    Column(        modifier = Modifier            .padding(innerPadding)            .consumeWindowInsets(innerPadding)            .verticalScroll(rememberScrollState())    ) { /* Content */ }}
<br />

RIGHT because fitInside fits the content to the IME insets regardless of contentWindowInsets.

kotlin
// RIGHTScaffold() { innerPadding ->    Column(        modifier = Modifier            .padding(innerPadding)            .consumeWindowInsets(innerPadding)            .fitInside(WindowInsetsRulers.Ime.current)            .verticalScroll(rememberScrollState())    ) { /* Content */ }}
<br />

RIGHT because the default contentWindowInsets does not contain IME insets, and imePadding() applies IME insets:

kotlin
// RIGHTScaffold() { innerPadding ->    Column(        modifier = Modifier            .padding(innerPadding)            .consumeWindowInsets(innerPadding)            .imePadding()            .verticalScroll(rememberScrollState())    ) { /* Content */ }}
<br />
WRONG

WRONG because there will be excess padding when the IME opens. IME insets are applied twice, once with innerPadding, which contains IME insets from the passed contentWindowInsets values, and once with imePadding:

kotlin
// WRONGScaffold(contentWindowInsets = WindowInsets.safeDrawing) { innerPadding ->    Column(        modifier = Modifier            .padding(innerPadding)            .imePadding()            .verticalScroll(rememberScrollState())    ) { /* Content */ }}
<br />

WRONG because the IME will cover up the content. Scaffold's default contentWindowInsets does NOT contain IME insets.

kotlin
// WRONGScaffold() { innerPadding ->    Column(        modifier = Modifier            .padding(innerPadding)            .verticalScroll(rememberScrollState())    ) { /* Content */ }}
<br />

IMEs without Scaffolds code patterns

RIGHT

The following code samples WILL NOT cause excessive padding.

kotlin
// RIGHTBox(    // Insets consumed    modifier = Modifier.safeDrawingPadding() // or imePadding(), safeContentPadding(), safeGesturesPadding()) {    Column(        modifier = Modifier.imePadding()    ) { /* Content */ }}
<br />
kotlin
// RIGHTBox(    // Insets consumed    modifier = Modifier.windowInsetsPadding(WindowInsets.safeDrawing) // or WindowInsets.ime, WindowInsets.safeContent, WindowInsets.safeGestures) {    Column(        modifier = Modifier.imePadding()    ) { /* Content */ }}
<br />
kotlin
// RIGHTBox(    // Insets not consumed, but irrelevant due to fitInside    modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // or WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()) {    Column(        modifier = Modifier            .fillMaxSize()            .fitInside(WindowInsetsRulers.Ime.current)    ) { /* Content */ }}
<br />
WRONG

The following code sample WILL cause excessive padding because IME insets are applied twice:

kotlin
// WRONGBox(    // Insets not consumed    modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()) // or WindowInsets.ime.asPaddingValues(), WindowInsets.safeContent.asPaddingValues(), WindowInsets.safeGestures.asPaddingValues()) {    Column(        modifier = Modifier.imePadding()    ) { /* Content */ }}
<br />

Navigation Bar Contrast & System Bar Icons

  • If the Activity uses enableEdgeToEdge from WindowCompat, you MUST set isAppearanceLightNavigationBars and isAppearanceLightStatusBars to the inverse of the device theme for apps that support light and dark theme so the system bar icons are legible. It's recommended to do this in your theme file. DO NOT do this if the Activities use enableEdgeToEdge from ComponentActivity because it handles the icon colors automatically.

    kotlin
    // Only use if calling `enableEdgeToEdge` from `WindowCompat`.// Apply to your theme file.@Composablefun MyTheme(    darkTheme: Boolean = isSystemInDarkTheme(),    content: @Composable () -> Unit) {    val view = LocalView.current    if (!view.isInEditMode) {        SideEffect {            val window = (view.context as? Activity)?.window ?: return@SideEffect            val controller = WindowCompat.getInsetsController(window, view)
                // Dark icons for Light Mode (!darkTheme), Light icons for Dark Mode            controller.isAppearanceLightStatusBars = !darkTheme            controller.isAppearanceLightNavigationBars = !darkTheme        }    }
        MaterialTheme(content = content)}
    <br />
  • If any screen uses a Scaffold or a NavigationSuiteScaffold with a bottom bar (e.g., BottomAppBar, NavigationBar), set window.isNavigationBarContrastEnforced = false in the corresponding Activity for SDK 29+. This prevents the system from adding a translucent background to the navigation bar, verifying your bottom bar colors extend to the bottom of the screen.

Lists

  • Apply inset padding (like Scaffold's innerPadding) to the contentPadding parameter of scrollable components (e.g. LazyColumn, LazyRow). DO NOT apply it as a Modifier.padding() to the list's parent container, as this clips the content and prevents it from scrolling behind the system bars.
  • Create a translucent composable covering the system bar so that the icons are still legible.
kotlin
class SystemBarProtectionSnippets : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)
        // enableEdgeToEdge sets window.isNavigationBarContrastEnforced = true        // which is used to add a translucent scrim to three-button navigation        enableEdgeToEdge()
        setContent {            MyTheme {                // Main content                MyContent()
                // After drawing main content, draw status bar protection                StatusBarProtection()            }        }    }}
@Composableprivate fun StatusBarProtection(    color: Color = MaterialTheme.colorScheme.surfaceContainer,) {    Spacer(        modifier = Modifier            .fillMaxWidth()            .height(                with(LocalDensity.current) {                    (WindowInsets.statusBars.getTop(this) * 1.2f).toDp()                }            )            .background(                brush = Brush.verticalGradient(                    colors = listOf(                        color.copy(alpha = 1f),                        color.copy(alpha = 0.8f),                        Color.Transparent                    )                )            )    )}
<br />

Dialogs

If both the following conditions are true, then the Dialog is full screen and must be made edge-to-edge:

  1. The DialogProperties contains usePlatformDefaultWidth = false.
  2. The Dialog calls Modifier.fillMaxSize().

To make a full screen Dialog edge-to-edge, set decorFitsSystemWindows = false in the DialogProperties.

kotlin
Dialog(    onDismissRequest = { /* Handle dismiss */ },    properties = DialogProperties(        // 1. Allows the dialog to span the full width of the screen        usePlatformDefaultWidth = false,        // 2. Allows the dialog to draw behind status and navigation bars        decorFitsSystemWindows = false    )) { /* Content */ }
<br />

Checklist

  • [ ] Does every Activity call enableEdgeToEdge()?
  • [ ] Is adjustResize set in the AndroidManifest.xml?
  • [ ] Does every TextField, OutlinedTextField, or BasicTextField have a parent with imePadding(), fitInside, Modifier.safeDrawingPadding(), Modifier.safeContentPadding(), Modifier.safeGesturesPadding(), or contentWindowInsets set to WindowInsets.safeDrawing or WindowInsets.ime?
  • [] Does the first and last list item draw away from the system bars by passing insets to contentPadding?
  • [] Do FABs draw above the navigation bars by either being inside a Scaffold or by applying Modifier.safeDrawingPadding()?
  • [] Does the project build? Run ./gradlew build to be sure.

來源與署名

來源:android/skills位於system/edge-to-edge提交42dc227

授權條款: Complete terms in LICENSE.txt

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架

更多來自 android/skills 的技能

Wear Compose M3

android

指導建置、更新與移轉 Wear OS Compose Material3 應用程式,並提供標準元件範例與版本規則。

Software Development7.7K13 天前更新

Display Glasses With Jetpack Compose Glimmer

android

Provides guidelines for developing projected Android XR apps for display glasses using the Jetpack Compose Glimmer UI toolkit. This skill covers foundational Glimmer design principles, workflows for implementing Jetpack Compose Glimmer, and interaction models for the glasses form factor. Use this skill to build an Android XR Augmented Experience app with Jetpack Compose Glimmer that adheres to the Glimmer design system for optimized glasses styling.

待分類7.7K13 天前更新

Play Billing Library Version Upgrade

android

引導將 Android 專案從舊版 Google Play 帳務程式庫升級到最新穩定版本。

Software Development7.7K13 天前更新

Leanback To Compose Tv Migration

android

Provides instructions and architectural patterns for migrating Android TV applications from legacy Leanback UI Toolkit, Android Views, or Support Fragments to Jetpack Compose for TV (androidx.tv). Use this skill for Leanback to Compose migrations, including browse screen, settings screen, authentication screen, login screen, or video playback screen migrations, or when replacing BrowseSupportFragment, LeanbackSettingsFragment, PreferenceFragment, BaseLeanbackPreferenceFragmentCompat, VideoSupportFragment, GuidedStepSupportFragment, SearchSupportFragment, VerticalGridSupportFragment, Presenter, ArrayObjectAdapter, or CursorMapper with modern Compose equivalents, implementing immersive carousels with focus memory, Media3 video playback with PlayerSurface, or custom 10-foot hero layouts.

待分類7.7K13 天前更新

Testing Setup

android

分析 Android 專案的測試設定,並安裝測試框架、測試基礎設施,以及單元、UI、截圖與端對端測試。

Software Development7.7K13 天前更新

Android Profiler

android

引導 Android 效能分析:錄製系統追蹤、堆積轉儲與方法記錄,並以 Perfetto SQL 進行分析。

Software Development7.7K13 天前更新