-
SafeArea는 항상 안전하지 않습니다AI Agent/flutter 2026. 7. 5. 15:00728x90반응형

내 폰에선 재현되지 않던 사용자 스크린샷 한 장 사용자가 보낸 스크린샷 한 장으로 시작됐습니다.
채팅 입력창이 키보드에 반쯤 가려져 있었습니다.
글자를 치는데 커서가 안 보인다는 제보였고, 스크린샷 속 기종은 제 손에 없는 안드로이드 폰이었습니다.
제 폰에선 몇 번을 눌러도 멀쩡하게 입력창이 키보드 위로 올라왔습니다.재현이 안 되는 버그가 제일 곤란합니다.
코드에는 분명 SafeArea가 들어가 있었고, 저는 그게 입력창을 알아서 밀어 올려줄 거라 믿고 있었습니다.
그 믿음이 틀렸다는 걸 아는 데 반나절이 걸렸습니다.제 폰에서만 재현이 안 됐습니다
처음엔 SafeArea를 더 촘촘히 감싸는 방향으로 손을 댔습니다.
입력창 바깥, 그 바깥의 컬럼까지 SafeArea로 겹겹이 둘렀습니다.
제 폰에선 여전히 잘 됐고, 제보한 사용자 쪽에선 여전히 가려졌습니다.
같은 코드가 기기에 따라 다르게 도는데, 저는 SafeArea만 계속 두껍게 바르고 있었습니다.문제는 SafeArea가 마법이 아니라는 데 있었습니다.
이 위젯은 노치나 상태바 같은 시스템 여백을 읽어서 그만큼 자식을 밀어주는 일을 합니다.
그런데 그 "여백"에 키보드는 포함되지 않습니다.
SafeArea가 참고하는 값과 키보드가 밀고 들어오는 값이 애초에 다른 통에 담겨 있었습니다.그걸 모르고 저는 한동안 헛발질을 했습니다.
SafeArea에 minimum을 줘서 아래쪽 여백을 강제로 확보해보기도 하고, bottom을 true로 명시했다가 false로 바꿔보기도 했습니다.
어떤 조합을 넣어도 제 폰에선 티가 안 났고, 제보한 쪽에선 여전히 가려졌습니다.
제 손에 그 기종이 없다는 게 계속 발목을 잡았습니다.
결국 사용자에게 부탁해서, 키보드를 올린 상태와 내린 상태 스크린샷을 한 장씩 더 받았습니다.
두 장을 나란히 놓고 나서야 입력창이 밀려나는 게 아니라 아예 안 밀린다는 걸 눈으로 확인했습니다.키보드는 padding이 아니라 viewInsets입니다
MediaQuery에는 비슷하게 생긴 값이 여러 개 있습니다.
저는 그동안 이걸 뭉뚱그려서 "화면 여백" 하나로만 생각했는데, 실제로는 성격이 다른 값이 나란히 들어 있었습니다.viewPadding은 노치나 상태바처럼 하드웨어가 가리는 고정 여백입니다.
SafeArea가 읽는 값이 이쪽입니다.
반대로 viewInsets는 키보드처럼 시스템 UI가 지금 화면을 덮은 크기입니다.
키보드가 올라오면 viewInsets.bottom이 커지고, 내려가면 0으로 돌아옵니다.
SafeArea는 viewInsets를 보지 않으니, 키보드가 아무리 올라와도 알 도리가 없었던 겁니다.final keyboard = MediaQuery.viewInsetsOf(context).bottom; // 키보드가 덮은 높이 Padding( padding: EdgeInsets.only(bottom: keyboard), child: const MessageInputBar(), )세 값을 화면에 나란히 찍어놓고 키보드를 올렸다 내렸다 해보고서야 그림이 잡혔습니다.
키보드를 올리면 viewInsets만 움직이고 viewPadding은 꿈쩍도 안 했습니다.
제가 반나절 동안 두껍게 바른 SafeArea는, 애초에 움직이지 않는 값을 붙들고 있었던 셈입니다.한 가지 더 헷갈렸던 건 padding과 viewPadding의 관계였습니다.
이름이 비슷해서 같은 값인 줄 알았는데, 키보드가 올라오면 padding.bottom은 0으로 줄고 viewPadding.bottom은 원래 값을 그대로 유지했습니다.
키보드가 하단 노치 영역을 덮어버리니, 지금 실제로 남은 여백(padding)은 사라지지만 하드웨어가 원래 요구하던 여백(viewPadding)은 변하지 않는다는 뜻이었습니다.
이 미묘한 차이를 모르고 padding을 참고했다면, 키보드가 올라올 때마다 하단 여백이 통째로 사라지는 또 다른 버그를 만들었을 겁니다.
그래서 저는 지금도 이 셋 중 무엇을 읽을지 헷갈리면 일단 셋을 다 찍어놓고 시작합니다.왜 제 폰에서만 멀쩡했는지는 아직도 자신이 없습니다
입력창을 viewInsets 기반으로 바꾸고 나니 제보가 멎었습니다.
그런데 여전히 남는 의문이 있습니다.
같은 SafeArea 코드인데 왜 제 폰에서는 처음부터 멀쩡했을까.
Scaffold의 resizeToAvoidBottomInset 기본 동작이 기기나 OS 버전에 따라 미묘하게 다르게 맞물렸던 것 같은데, 이건 제 추측이고 끝까지 똑 떨어지게 확인하진 못했습니다.그래도 하나는 손에 남았습니다.
이제 화면이 무언가에 가린다는 제보를 받으면, SafeArea를 더 두르기 전에 MediaQuery의 어떤 값이 그걸 책임지는지부터 찾습니다.
노치가 가린 건지, 키보드가 덮은 건지, 아니면 둘 다인지.
세 값의 이름이 헷갈릴 때마다 저는 아직도 화면에 숫자를 찍어놓고 키보드를 한 번 올려봅니다.원문 기준
원문 기준: https://api.flutter.dev/flutter/widgets/SafeArea-class.html
원문 본 날짜: 2026-07-01.
728x90반응형'AI Agent > flutter' 카테고리의 다른 글
로컬 상태는 서버 상태의 대체물이 아니라 임시 복사본입니다 (0) 2026.07.06 Flutter 라우팅은 화면 이동이 아니라 상태와 권한의 계약입니다 (0) 2026.07.05 Positioned 애니메이션이 프레임을 태우는 이유 (0) 2026.07.05 화면 밖으로 나간 Flutter 상태가 언제 살아남는지 정해야 합니다 (0) 2026.07.04 Flutter 모델 생성 코드는 소스가 아니라 산출물입니다 (0) 2026.07.04