skip to content

In a Flutter app targeting Android 15 or later, what does the default SystemUiMode.edgeToEdge change, and what stops working?

level: middleimportance: should knowfreq 26%

answer

  1. draw behind the system bars
  2. 3.27 targeted Android 15
  3. 3.47 targetSdkVersion 36
  4. statusBarColor no longer applies
  5. windowOptOutEdgeToEdgeEnforcement is temporary

basics

~20 s

Edge-to-edge makes the app draw behind transparent status and navigation bars. On Android 15+ targets statusBarColor and systemNavigationBarColor stop working, other SystemUiModes need an opt-out, and Android 16 targets, the Flutter 3.47 default, cannot opt out.

solid answer

~30 s

`SystemUiMode.edgeToEdge` means the Android status and navigation bars are transparent overlays drawn over the app's own content. Android enforces it for apps targeting API 35; Flutter 3.27 made API 35 the default target, and Flutter 3.47 sets `flutter.targetSdkVersion` to 36, where Android offers no opt-out. As a result `SystemUiOverlayStyle.statusBarColor` and `systemNavigationBarColor` have no effect, so you paint the background yourself and set icon brightness through `AnnotatedRegion<SystemUiOverlayStyle>` or `SystemChrome.setSystemUIOverlayStyle`. Flutter's docs say `leanBack`, `immersive`, `immersiveSticky` and `manual` need the `windowOptOutEdgeToEdgeEnforcement` style attribute on API 35 and are ignored on API 36+. Content also needs inset padding.

code

dart · 19 lines
dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class CallBackdrop extends StatelessWidget {
  const CallBackdrop({super.key, required this.child});

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return AnnotatedRegion<SystemUiOverlayStyle>(
      value: const SystemUiOverlayStyle(
        statusBarIconBrightness: Brightness.light,
        systemNavigationBarIconBrightness: Brightness.light,
      ),
      child: ColoredBox(color: Colors.black, child: child),
    );
  }
}

go deeper

for a junior

Remember that on modern Android the app draws behind see-through system bars, so it must paint what shows beneath them.

for a middle

Explain which SystemUiOverlayStyle fields stop working, how AnnotatedRegion sets icon brightness, and the role of the target SDK version.

for a senior

Show how you migrate an app that tinted bars or used fullscreen modes, and why the Android 15 opt-out is a dead end for Android 16 targets.

for a principal

Plan the rollout across screens and devices when a Flutter upgrade raises the target SDK, including which fullscreen features need product changes.

## What edge-to-edge means In **edge-to-edge** display the app draws behind the Android status bar and navigation bar, which become transparent overlays on top of its content. Flutter exposes the system UI modes through **`SystemChrome.setEnabledSystemUIMode(SystemUiMode mode, {List<SystemUiOverlay>? overlays})`** in `package:flutter/services.dart`. The enum `SystemUiMode` has five values: | Value | Behaviour | |---|---| | `leanBack` | Fullscreen; bars return when the user taps; the tap is not delivered to the app | | `immersive` | Fullscreen; bars return on an edge swipe that the app does not receive | | `immersiveSticky` | Fullscreen; bars return on an edge swipe that the app does receive | | `edgeToEdge` | Bars always visible, drawn over app content | | `manual` | You choose the overlays with `SystemUiOverlay.top` and `.bottom` | ## Why it became the default Android enforces edge-to-edge for apps that **target** Android 15 (API 35) or later, and Android 16 (API 36) removed the opt-out. Flutter's generated Android project targets `flutter.targetSdkVersion`, so the Flutter version decides the target: - **Flutter 3.27** moved the default target to Android 15, which switched every app that kept the default to edge-to-edge. - **Flutter 3.47** sets `flutter.targetSdkVersion` to **36**, so a default project targets Android 16 and **cannot opt out**. iOS also uses edge-to-edge as its default mode. ## What stops working Under edge-to-edge on an Android 15+ target: - **Bar colours.** `SystemUiOverlayStyle.statusBarColor` and `systemNavigationBarColor` no longer take effect; the bars are transparent over whatever the app paints. Set a background behind them instead, and keep the icons readable with `statusBarIconBrightness` and `systemNavigationBarIconBrightness`. - **Other modes.** Flutter's API docs state that with an Android 15 target, `leanBack`, `immersive`, `immersiveSticky` and `manual` work only after the opt-out migration, and that with an Android 16+ target the system ignores them. - **Layout assumptions.** Content now sits under the bars unless the app pads it. Handling those insets is a layout concern (safe areas and view padding), not part of the system UI API. ## The Android 15 opt-out For a project that still targets Android 15, the migration guide adds one attribute to the launch and normal themes in `android/app/src/main/res/values/styles.xml` (and the `values-night` copy): ```xml <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> ``` The guide warns that Android treats this as temporary and that using it on Android 16 can crash the app; version-specific resources avoid that. For a 3.47 project with the default target, the only durable answer is to lay the app out edge-to-edge. ## Styling the bars today Two ways set a `SystemUiOverlayStyle`: 1. **Declaratively**, by wrapping a screen in `AnnotatedRegion<SystemUiOverlayStyle>`. An `AppBar` already creates one, so do not wrap an `AppBar` in another; the docs describe which one wins for which bar. 2. **Imperatively**, with `SystemChrome.setSystemUIOverlayStyle(...)`, for example once in `main`. To react when bars appear or disappear in fullscreen modes, `SystemChrome.setSystemUIChangeCallback` reports whether overlays are visible; in `edgeToEdge` the overlays are always visible and do not change, so it has nothing to report. ## Checklist for an Android 15+ Flutter app - Remove code that paints the status bar via `statusBarColor`; paint the app background instead. - Set icon brightness per screen through `AnnotatedRegion` so light and dark screens stay legible. - Audit every screen for content hidden under the bars. - Do not ship `immersiveSticky` for video playback assuming it hides the bars on every device; test on an Android 16 device. ## Why Flutter cannot simply paint the bars Flutter owns every pixel inside its view, but the status bar and navigation bar are drawn by the operating system **above** that view. `SystemChrome` does not draw anything: `setEnabledSystemUIMode` and `setSystemUIOverlayStyle` send requests to the host over the `SystemChannels.platform` method channel, and the Android embedding applies them to the window. When the platform ignores a request, as it does for bar colours under enforced edge-to-edge, no Flutter code can override it. That is why the durable fix is always in the app's own layout and background, never in a different `SystemUiOverlayStyle` value.

  • How does a Flutter app on an Android 15 target opt out of edge-to-edge, and why is that not a lasting fix?
    Add `android:windowOptOutEdgeToEdgeEnforcement` set to `true` to the launch and normal themes in `styles.xml` and its `values-night` copy. Android treats it as temporary, Android 16 targets ignore it, and the Flutter guide warns it can crash on Android 16 without version-specific resources.
  • Where should a Flutter screen set status bar icon brightness if it has an AppBar?
    Through the `AppBar` itself, for example its `systemOverlayStyle`, because an `AppBar` already creates an `AnnotatedRegion<SystemUiOverlayStyle>`. Wrapping it in another region makes the two compete, and the docs describe which one wins for the status bar and which for the navigation bar.

saying these in an interview costs you the question

  • Setting statusBarColor still tints the bar under edge-to-edge.
  • Flutter draws the Android status bar itself, so it can recolour it.
  • The opt-out style attribute works on every Android version.
  • Edge-to-edge only happens if the app calls setEnabledSystemUIMode.
  • immersiveSticky is guaranteed to hide the bars on Android 16 targets.