[Flutter] Android 15에서 edge-to-edge로 인해 앱 화면이 가려지는 이슈

앱이 Android 15(API수준 35) 이상을 타겟팅해야 함

얼마 전에 구글플레이에서 다음과 같은 메일을 받았다.

 

콘솔에서 더 자세히 확인해 봤더니 2025년 8월 31일까지

프로덕션 앱을 안드로이드 15 (API 수준 35) 이상을 타겟팅하지 않으면

더 이상 앱을 업데이트할 수 없다고 한다.

 

flutterCompileSdkVersion = '35'
flutterTargetSdkVersion = '35'

그래서 기존 `34`였던 sdk 버전을 `35`로 올리고 앱을 다시 게시했다.

 

Android 15 Edge-to-edge

버전 업데이트 후 기능이 잘 작동하는지 이것저것 확인해 보던 중

Bottom sheet를 올렸을 때 화면 하단의 내비게이션바와 앱 화면이 겹치는 것을 확인했다.

 

사용자들이 텍스트를 확인하거나 버튼을 누르는 데 불편함이 있을 거 같아서 

내비게이션 바가 Bottom sheet 영역을 침범하지 않도록 수정하기로 했다.

 

일단 이유를 찾아보니 Android 15 이상을 실행하는 기기에서 SDK 35 이상을 타겟팅하면

앱이 자동으로 더 넓은 화면으로 표시된다고 한다.

 

예를 들면 오른쪽 예시와 같이 콘텐츠를 더 넓은 화면에 보여줄 수 있다.

 

하지만 앱의 상단 및 하단 영역이 상태 표시줄과 탐색 메뉴 뒤에 그려지기 때문에

앱의 일부가 가려지는 것을 원하지 않으면 별도로 인셋이나 패딩 설정을 해야 한다.

 

참고로 인셋은 화면의 특정 영역이 시스템 UI(상태 바, 내비게이션 바 등)와 겹치는 경우에

앱의 콘텐츠가 시스템 UI와 겹치는 위치를 정의하는 값을 의미한다.

 

앱 콘텐츠와 내비게이션 바가 겹치는 문제 해결하기

Bottom sheet가 내비게이션 바와 겹치는 문제

앱이 Material 3 Component(예시: `TopAppBar`, `BottomAppBar`, `NavigationBar`)를 사용하는 경우에는

인셋을 자동으로 처리하므로 추가 작업이 필요하지 않다. 

 

padding 적용 전/후

하지만 `BottomSheet` 등 특정 위젯의 경우에는 

시스템 표시줄이 앱의 화면과 겹치는 것을 원하지 않는다면 별도로 인셋이나 패딩을 적용해야 한다.

 

이렇게 하면 화면에 표시된 시스템 UI (예: 내비게이션 바)가 차지하는 영역만큼

Bottom Sheet가 하단에 패딩값을 가지기 때문에 내비게이션 바에 가려지지 않는다.

 

showModalBottomSheet(
    context: context,
    builder: (BuildContext context) {
      return Padding(
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).padding.bottom, // ✅ 하단 가려지는 문제 해결
        ),
        child: Container(
          padding: const EdgeInsets.symmetric(
            vertical: 12,
            horizontal: 20,
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              ListTile(
                leading: const Icon(Icons.camera_alt),
                title: const Text('사진 촬영하기'),
                onTap: () {
                  getAndSetImage(ImageSource.camera);
                  Navigator.of(context).pop();
                },
              ),
              ListTile(
                leading: const Icon(Icons.photo_library),
                title: const Text('갤러리에서 가져오기'),
                onTap: () {
                  getAndSetImage(ImageSource.gallery);
                  Navigator.of(context).pop();
                },
              ),
            ],
          ),
        ),
      );
    },
);

`Container`를 `Padding` 위젯으로 감싸고 `MediaQuery.of(context).padding.bottom`을 추가했다.

 

이렇게 하면 화면에 표시된 시스템 UI (예: 내비게이션 바)가 차지하는 영역만큼

Bottom Sheet가 하단에 패딩값을 가지기 때문에 내비게이션 바에 가려지지 않는다.

 

스크린이 내비게이션 바와 겹치는 문제

SafeArea 적용 전/후

그런데 `BottomNavigationBar`를 사용하지 않는 스크린에서도

하단 내비게이션 바에 앱 화면이 가려지는 것을 발견했다.

 

Scaffold(
  body: SafeArea(
    child: MyPageContent(),
  ),
);

이런 경우에는 간단하게 `Scaffold`의 하위 위젯을 `SafeArea`로 감싸면 된다.

 

`SafeArea` 위젯은 노치, 상태 바, 하단 내비게이션 바시스템 UI 요소에 의해 가려지지 않도록

자동으로 padding을 적용해서 안전한 영역을 확보해 주는 위젯이다.

 

플러터에서 edge-to-edge 비활성화하기

Flutter 앱이 Android SDK 버전 15를 타겟팅하는 경우, 앱이 자동으로 edge-to-edge 모드로 표시된다. 

Flutter 3.27 버전부터는 edge-to-edge를 옵트아웃할 수도 있다.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application ...>
        <activity ...>
            <!-- ✅Style to modify: -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
            />
        </activity>
    </application>
</manifest>

`android/app/src/main/AndroidManifest.xml`에 기본 스타일을 수정하는 `meta-data`를 추가한다.

 

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
        ...
        <!-- ✅Add the following line: -->
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
    </style>
    ...
    <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
        ...
	      <!-- ✅Add the following line: -->
        <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
    </style>
</resources>

스타일이 정의된 파일 `android/app/src/main/res/values/styles.xml`에서 다음 속성을 추가하면

Android SDK 15를 타겟으로 하는 앱의 edge-to-edge 기능이 해제된다.

 

참고 문서