Trình bày Modal là một cách hiển thị màn hình phía trên ngữ cảnh hiện tại, chặn tương tác với giao diện trước đó. Trong phát triển di động, cửa sổ modal được sử dụng cho các tác vụ tập trung: nhập dữ liệu, xác nhận hành động, ủy quyền và chọn tùy chọn. Theo Apple HIG, 2025, các trình bày modal không nên chiếm quá 20% kịch bản điều hướng trong ứng dụng. Trong Android, tính modal được thực hiện thông qua DialogFragment, BottomSheet và Activity với các cờ khởi chạy cụ thể.
Những điểm chính
Trình bày Modal là một mẫu điều hướng trong đó màn hình mới xuất hiện phía trên màn hình hiện tại, tạm thời chặn tương tác với nội dung cha. Người dùng phải hoàn thành rõ ràng tác vụ modal (hủy, lưu, đóng) để quay lại trạng thái trước đó.
Tính modal giải quyết một nhiệm vụ nhận thức: nó tập trung sự chú ý của người dùng vào một hành động duy nhất mà không bị phân tâm bởi phần còn lại của giao diện. Điều này cực kỳ quan trọng đối với biểu mẫu đăng ký, hộp thoại xác nhận, chọn tệp và ủy quyền qua các dịch vụ bên thứ ba. Human Interface Guidelines của Apple khuyên chỉ nên sử dụng tính modal cho các tác vụ cần hoàn thành trước khi tiếp tục.
Không giống cửa sổ modal web, Trình bày Modal di động có thể toàn màn hình (chiếm toàn bộ màn hình) hoặc một phần (Page Sheet, Bottom Sheet). Việc chọn loại phụ thuộc vào ngữ cảnh tác vụ và quy ước nền tảng. iOS thiên về Page Sheet cho hầu hết các kịch bản, để Full Screen cho trình phát video và trình chỉnh sửa ảnh.
Push Presentation (điều hướng ngăn xếp) thêm màn hình vào ngăn xếp điều hướng và tự động hiển thị nút quay lại. Người dùng có thể quay lại màn hình trước bất kỳ lúc nào. Trình bày Modal, ngược lại, yêu cầu hoàn thành rõ ràng: nút quay lại không có hoặc đóng cửa sổ modal thay vì quay lại màn hình trước.
Sự khác biệt chính giữa trình bày modal và push: Trình bày Modal chặn điều hướng quay lại mà không mất dữ liệu, yêu cầu một hành động để đóng (Save, Cancel, Done) và thường đại diện cho một tác vụ riêng biệt. Push Presentation bảo toàn hệ thống phân cấp điều hướng, tự động thêm nút quay lại và phù hợp cho việc xem nội dung tuần tự.
| Đặc điểm | Trình bày Modal | Push Presentation |
|---|---|---|
| Chặn quay lại | Có, yêu cầu hành động rõ ràng | Không, nút quay lại luôn có sẵn |
| Sử dụng điển hình | Biểu mẫu, ủy quyền, lựa chọn | Xem chi tiết, điều hướng |
| Hoạt ảnh | Từ dưới lên (iOS), trượt (Android) | Từ phải sang trái (iOS) |
| Ngăn xếp điều hướng | Không được thêm vào ngăn xếp chính | Được thêm vào ngăn xếp |
Trong thực tế, việc chọn giữa Modal và Push phụ thuộc vào ngữ cảnh. Khuyên nên sử dụng tính modal cho các tác vụ mà người dùng phải hoàn thành trước khi tiếp tục và Push cho việc khám phá nội dung tuần tự. Trộn lẫn các mẫu trên cùng một màn hình dẫn đến nhầm lẫn và làm giảm trải nghiệm người dùng.
iOS cung cấp một số kiểu trình bày modal thông qua liệt kê UIModalPresentationStyle. UIKit hỗ trợ .fullScreen (toàn màn hình), .pageSheet (thẻ với lề trên), .formSheet (cửa sổ trung tâm trên iPad) và .automatic (hệ thống chọn dựa trên ngữ cảnh). Kể từ iOS 13, kiểu mặc định là .automatic, kiểu này chọn .pageSheet cho iPhone.
Phương thức UIKit cơ bản cho trình bày modal là present(_:animated:completion:). Bộ điều khiển gọi phương thức trở thành presentingViewController và bộ điều khiển mới trở thành presentedViewController. Việc đóng được thực hiện thông qua dismiss(animated:completion:). SwiftUI cung cấp bộ sửa đổi .sheet cho hành vi tương tự.
Cách tiếp cận khai báo của SwiftUI sử dụng các bộ sửa đổi .sheet và .fullScreenCover. Cái đầu tiên tạo Page Sheet, cái thứ hai tạo trình bày modal toàn màn hình. Cả hai đều chấp nhận một ràng buộc với Bool hoặc đối tượng có thể nhận dạng kiểm soát khả năng hiển thị của cửa sổ modal. Việc đóng xảy ra khi ràng buộc được đặt thành false hoặc khi gọi dismiss từ môi trường.
struct ContentView: View {
@State private var showModal = false
var body: some View {
Button("Mở biểu mẫu") {
showModal = true
}
.sheet(isPresented: $showModal) {
RegistrationForm()
}
}
}
struct RegistrationForm: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("Lưu") { dismiss() }
}
}
Android không có API duy nhất cho trình bày modal như iOS. Thay vào đó, nền tảng cung cấp một số cơ chế: DialogFragment cho cửa sổ hộp thoại, BottomSheetDialogFragment cho bảng dưới và Activity với cờ NEW_TASK và CLEAR_TOP cho màn hình modal. Jetpack Compose đã giới thiệu một thành phần Dialog thống nhất cho tất cả các loại cửa sổ modal.
DialogFragment là lớp cơ sở cho cửa sổ modal trong Android SDK. Nó quản lý vòng đời của hộp thoại, xử lý xoay màn hình và lưu trạng thái. Fragment hiển thị phía trên Activity mà không chặn ngăn xếp điều hướng. Việc đóng được thực hiện thông qua dismiss() hoặc chạm bên ngoài vùng hộp thoại nếu setCancelable(true).
BottomSheetDialogFragment hiển thị cửa sổ modal dưới dạng bảng nâng lên từ dưới. Mẫu này phổ biến trong Material Design để chọn tùy chọn, chia sẻ và các hành động nhanh. BottomSheet có thể có chiều cao cố định hoặc có thể mở rộng (chiều cao xem trước + chiều cao đầy đủ). Trong Compose, ModalBottomSheet từ thư viện Material3 được sử dụng.
@Composable
fun ModalScreen(onDismiss: () -> Unit) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier.padding(16.dp)
) {
Column {
Text("Biểu mẫu modal", style = MaterialTheme.typography.headlineSmall)
Button(onClick = onDismiss) {
Text("Đóng")
}
}
}
}
}
Cửa sổ modal là một công cụ UX mạnh mẽ, nhưng việc sử dụng quá mức sẽ làm giảm trải nghiệm người dùng. Apple HIG và Google Material Design thống nhất về các khuyến nghị: tính modal nên được sử dụng cho các tác vụ tập trung và không vượt quá 20% tổng số hành động điều hướng.
Cửa sổ modal phù hợp cho các kịch bản: nhập dữ liệu (biểu mẫu đăng ký, hồ sơ), xác nhận (xóa, gửi), lựa chọn (trình chọn ngày, quản lý tệp) và ủy quyền (OAuth, Firebase Auth). Nếu tác vụ mất ít hơn 30 giây và yêu cầu chặn ngữ cảnh, hãy chọn tính modal.
Không sử dụng trình bày modal cho: xem nội dung tuần tự (sử dụng Push), hiển thị lỗi (sử dụng Toast hoặc Snackbar), quảng cáo và ưu đãi khuyến mại mà không có yêu cầu rõ ràng từ người dùng. Material Design khuyên tránh các cửa sổ modal lồng nhau — điều này làm người dùng mất phương hướng và phá vỡ hệ thống phân cấp điều hướng.
Đối với cửa sổ modal có trường văn bản, hãy đảm bảo xử lý việc mất tiêu điểm bàn phím. Khi bàn phím xuất hiện, cửa sổ modal nên dịch chuyển lên trên để người dùng có thể thấy văn bản đang nhập. UIKeyboardWillShowNotification trong iOS và adjustResize trong Android giải quyết nhiệm vụ này.
Hãy xem việc triển khai trình bày modal trên cả hai nền tảng. Ví dụ Swift cho thấy cấu hình UIModalPresentationStyle.pageSheet với ủy quyền để xử lý việc đóng. Ví dụ Kotlin minh họa DialogFragment với bố cục tùy chỉnh và bảo toàn trạng thái.
let modalVC = ModalViewController()
modalVC.modalPresentationStyle = .pageSheet
if let sheet = modalVC.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
}
modalVC.presentationController?.delegate = self
present(modalVC, animated: true)
// MARK: - UIAdaptivePresentationControllerDelegate
extension ViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("Đã đóng modal")
}
}
class ModalDialogFragment : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_modal, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<Button>(R.id.closeButton).setOnClickListener {
dismiss()
}
}
}
Câu hỏi thường gặp
Sử dụng Trình bày Modal cho các tác vụ tập trung mà người dùng phải hoàn thành trước khi tiếp tục: biểu mẫu, xác nhận, ủy quyền. Push phù hợp cho việc xem nội dung tuần tự nơi người dùng có thể tự do điều hướng quay lại. Cửa sổ modal không nên chứa điều hướng bên trong.
Kể từ iOS 13, kiểu mặc định .automatic chọn .pageSheet cho iPhone. .pageSheet phù hợp cho hầu hết các kịch bản (biểu mẫu, chi tiết). .fullScreen dành cho nội dung đa phương tiện (video, trình chỉnh sửa ảnh). .formSheet dành cho ứng dụng iPad cần cửa sổ trung tâm.
Jetpack Compose cung cấp thành phần Dialog cho cửa sổ modal đơn giản và ModalBottomSheet cho bảng dưới. Dialog chấp nhận onDismissRequest và nội dung theo kiểu Compose. Sử dụng rememberSaveable bên trong hộp thoại để bảo toàn trạng thái.
Apple HIG và Material Design không khuyên dùng cửa sổ modal lồng nhau. Nếu người dùng mở cửa sổ modal phía trên một cửa sổ modal khác, họ sẽ mất ngữ cảnh và có thể nhầm lẫn về hệ thống phân cấp. Thay vì lồng nhau, hãy sử dụng Chỉ báo Bước hoặc mẫu Trình hướng dẫn với một cửa sổ modal duy nhất.
Sử dụng UIAdaptivePresentationControllerDelegate trong iOS (phương thức presentationControllerShouldDismiss) hoặc OnBackPressedDispatcher trong Android. Khi có các thay đổi chưa được lưu, hãy hiển thị AlertDialog với các tùy chọn: lưu, hủy thay đổi hoặc ở lại màn hình. Điều này ngăn ngừa việc mất dữ liệu do người dùng vô tình.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm