Skip to content

App Safe-Area / Window-Insets

app-layout specs/app-layout/safe-area.kmd

Consumo obrigatório de window-insets (status bar, nav bar, display cutout, IME) em todos os apps Koder. Implementação canônica: KoderSafeScaffold / KoderApp(safeArea: true) em engines/sdk/koder_kit v0.3.0+, Modifier.safeDrawingPadding() no Android nativo Compose, env(safe-area- inset-*) no web.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — App safe-area / window-insets handling

Applicability

All Koder apps (mobile, desktop, TV, web PWA) — any surface where system chrome (status bar, navigation bar, display cutout, on-screen keyboard, title bar, task bar) can occlude rendered content.

Not applicable to CLIs, landing pages (static HTML with normal document flow), or pure server components.

Required behaviour

  1. Always consume OS-reported insets. The OS knows the exact pixel dimensions of system chrome on the current device and updates them live (orientation change, gesture nav vs 3-button, keyboard open/close, foldable unfold, PiP resize). Apps must consume these values — never hard-code pixel margins.
  2. No content occluded by system chrome at rest. Every interactive element — buttons, text fields, list items, sliders — must be fully tappable without fighting the nav bar, status bar, or cutout.
  3. Text under the status bar is forbidden unless the design is deliberately edge-to-edge (splash screen, full-screen video, camera viewfinder) and the content is non-critical.
  4. IME (on-screen keyboard) handling. Forms must resize or scroll so the focused field stays visible above the keyboard. Default to resizeToAvoidBottomInset: true (Flutter) / WindowInsets.ime (native Android) / keyboardShouldPersistTaps='handled' (Web).
  5. Edge-to-edge on Android 15+ is the platform default. Apps targeting API ≥ 35 must handle insets explicitly; the OS no longer adds automatic padding.

Platform primitives

PlatformAPINotes
Android native (API 30+)WindowInsets.getInsets(WindowInsets.Type.systemBars())Returns Insets(left, top, right, bottom) in px
Android native (compat)ViewCompat.getRootWindowInsets(view) + WindowInsetsCompatAPI 21+
Jetpack ComposeWindowInsets.systemBars, .navigationBars, .statusBars, .safeDrawing, .imeStateful; recomposes on change
Jetpack ComposeModifier.safeDrawingPadding(), .systemBarsPadding(), .imePadding()Apply padding automatically
Jetpack ComposeScaffold(contentWindowInsets = WindowInsets.safeDrawing)Handles all edges + IME at the Scaffold level
Jetpack Compose (Activity)enableEdgeToEdge() before setContentRequired for targetSdk ≥ 35
FlutterMediaQuery.of(context).viewPaddingEdgeInsets in logical pixels
FlutterSafeArea(child: …) widgetConsumes padding; minimum: sets a floor
FlutterScaffold(resizeToAvoidBottomInset: true)IME handling
iOS nativeUIWindow.safeAreaInsets, safeAreaLayoutGuideEquivalent to Android systemBars
Web (CSS)env(safe-area-inset-{top,right,bottom,left})Required for iOS Safari PWA + Android WebView notch devices; fallback to 0 on older browsers
Web (CSS)<meta name="viewport" content="viewport-fit=cover">Prerequisite for env(safe-area-inset-*) to produce non-zero values on iOS

SDK widgets (approved implementations)

Apps must use the SDK-provided widget where available instead of re-implementing per edge. Using the SDK is the spec's deterministic compliance test.

PlatformSDKWidget / API
Flutter (Android / iOS / Linux / macOS / Windows)engines/sdk/koder_kit v0.3.0+KoderSafeScaffold(appBar:, body:, floatingActionButton:, …) — drop-in replacement for Scaffold with SafeArea pre-wired + optional per-edge toggles + minimumPadding: floor
Flutterengines/sdk/koder_kit v0.3.0+KoderApp(safeArea: true) — root-level SafeArea for apps that build their own Scaffolds; default true
Webengines/sdk/koder_web_kit (planned)<koder-safe-layout> custom element + baseline CSS vars --pad-top, --pad-bottom consuming env(safe-area-inset-*)
Native Android (Kotlin/Compose)koder-kit-android (deferred)KoderSafeScaffold { } composable; until the lib exists, apply the pattern below manually

Native Android Kotlin — inline pattern

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            KoderEyeTheme(...) {
                Surface(modifier = Modifier.fillMaxSize(), ...) {
                    Column(
                        modifier = Modifier
                            .fillMaxSize()
                            .safeDrawingPadding()     // ← consume all insets
                            .verticalScroll(...)
                            .padding(16.dp)
                    ) { ... }
                }
            }
        }
    }
}

Edge-to-edge opt-out (rare)

Apps with genuine edge-to-edge content (video players, splash screens, maps) pass safeArea: false on KoderApp and handle individual edges per screen — e.g. keep the video surface under the status bar but consume bottomPadding for playback controls.

Deterministic audit checks

/k-housekeep audit (ticket engines/sdk/koder_kit#003) grep-verifies:

  1. Every Flutter app with koder_kit in pubspec.yaml imports KoderApp.
  2. Every Scaffold(body: …) at the app entry point uses either SafeArea or KoderSafeScaffold. A raw Column / ListView / Stack at body: position triggers a warning.
  3. Every landing / web app that ships koder-web-kit.js also references koder-web-kit.css (or equivalent providing --pad-top vars).
  4. KoderApp(safeArea: false) presence triggers an info-level annotation: "edge-to-edge opt-in — verify manually that insets are consumed per screen".

Rationale

This spec exists because the pattern is rediscovered per-app, forgotten in half of them, and leads to clipped UI that is only caught in screenshot review. Encapsulating it in the SDK reduces the cost from every app re-reads this spec to every app imports one symbol, and the audit makes compliance verifiable without human review. See engines/sdk/koder_kit/docs/rfcs/RFC-001-spec-encapsulation-across-platforms.md §2.1 for the AI-token-savings argument.

Canonical reference

engines/sdk/koder_kit/lib/src/safe_scaffold.dart — Flutter reference. products/dev/eye/app/android/app/src/main/kotlin/dev/koder/eye/MainActivity.kt — native Kotlin reference pattern.

  • ../themes/light-dark.kmd — companion cross-cutting UI spec
  • ../koder-app/behaviors.kmd — parent behaviors spec

References