Bạn bọc màn hình bằng SafeArea rồi cộng thêm padding theo status bar, kết quả nội dung cách mép trên quá xa. Hoặc một widget con đọc MediaQuery.padding.top bằng 0 và bị hiểu nhầm rằng máy không có vùng cần tránh.
Bài đối chiếu Flutter API ngày 01/11/2026. Mẫu dùng Material, tập trung quan hệ giữa SafeArea và MediaQuery; chưa chạy Flutter build, widget test hoặc thiết bị. Các bước kiểm tra bên dưới là cách bạn xác nhận trên cấu hình ứng dụng thực tế.
1. SafeArea xử lý phần inset cho child
SafeArea thêm khoảng đệm để child tránh các vùng bị hệ điều hành hoặc hình dạng màn hình che khuất. Bạn có thể bật hoặc tắt việc xử lý từng cạnh tùy bố cục. Không phải mọi màn hình đều cần thêm một SafeArea mới ở mọi cấp.
Sau khi xử lý một cạnh, widget điều chỉnh MediaQuery cho subtree để cạnh đã được tránh không tiếp tục được tính như ban đầu. Vì vậy, một widget con thấy padding.top bằng 0 có thể đang nằm trong vùng đã được xử lý, không phải đang đọc thông tin sai.
2. Minimum chọn mức lớn hơn
Với minimum, SafeArea dùng mức lớn hơn giữa khoảng tối thiểu và safe padding tương ứng. Nó không mặc định cộng hai giá trị lại. Nếu bạn muốn thêm khoảng cách thiết kế ngoài phần an toàn, hãy quyết định điều đó ở lớp layout riêng thay vì trộn hai khái niệm.
Một ví dụ số học: minimum 16 và safe padding 24 dẫn tới khoảng đệm 24 ở cạnh đó, không phải 40. Đây là diễn giải quy tắc trong tài liệu, không phải số đo đã thu từ một thiết bị.
3. Quan sát context trước và sau SafeArea
Đặt đoạn dưới vào lib/main.dart của project Flutter Material có sẵn. Màn hình không có AppBar để dễ quan sát cạnh trên; không lấy vị trí này làm quy tắc cho mọi màn hình có navigator hoặc header.
import 'package:flutter/material.dart';
void main() => runApp(const MaterialApp(home: SafeDemo()));
class SafeDemo extends StatelessWidget {
const SafeDemo({super.key});
@override
Widget build(BuildContext context) {
final before = MediaQuery.of(context).padding;
return Scaffold(
body: SafeArea(
minimum: const EdgeInsets.all(16),
child: Builder(
builder: (insideContext) {
final after = MediaQuery.of(insideContext).padding;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text('Padding top trước: ${before.top.toStringAsFixed(0)}'),
Text('Padding top trong: ${after.top.toStringAsFixed(0)}'),
const SizedBox(height: 16),
Expanded(
child: ListView(
children: const [
ListTile(title: Text('Đọc đúng context')),
ListTile(title: Text('Không cộng inset hai lần')),
ListTile(title: Text('Thử cả xoay màn hình')),
],
),
),
FilledButton(
onPressed: () {},
child: const Text('Nút minh họa ở cuối vùng an toàn'),
),
],
);
},
),
),
);
}
}
Builder cung cấp context nằm dưới SafeArea. Nếu dùng lại context bên ngoài, bạn đang hỏi dữ liệu của cấp khác. Nút chỉ minh họa vị trí layout và không thực hiện nghiệp vụ.
4. Kiểm tra cùng các lớp layout có sẵn
Thử trên thiết bị có cutout, xoay ngang, đổi kích thước cửa sổ và bật cỡ chữ lớn. Quan sát số padding ở hai context cùng vị trí nội dung. Giá trị cụ thể tùy môi trường; bài chưa có ảnh chụp hoặc kết quả chạy thiết bị để đưa ra một con số cố định.
Nếu thêm AppBar hoặc một lớp đã xử lý inset, kiểm tra lại cấp nào đang chịu trách nhiệm cho mỗi cạnh. Một khoảng trống có thể đến từ padding thiết kế, AppBar hoặc minimum, chứ không chỉ từ SafeArea.
Nếu muốn nền hoặc ảnh kéo dưới cutout nhưng phần chữ vẫn an toàn, có thể để lớp nền ngoài SafeArea và chỉ bọc phần tương tác cần tránh. Quyết định này thuộc thiết kế màn hình, không nên tắt cả bốn cạnh chỉ để xóa một khoảng trắng chưa tìm rõ nguồn.
Bàn phím là một thông tin khác
MediaQuery.viewInsets mô tả vùng bị che hoàn toàn, thường liên quan bàn phím. Nó khác padding và viewPadding. SafeArea không phải lời giải tổng quát cho mọi form bị bàn phím che; còn cần xem cách Scaffold và vùng cuộn xử lý kích thước còn lại.
Khi màn hình có form, kiểm tra riêng lúc bàn phím đóng và mở. Không cộng đồng thời nhiều nguồn bottom inset nếu chưa xác định lớp nào đã xử lý. Mẫu này không triển khai form hoặc thay thế bài kiểm tra keyboard trên thiết bị.

