Trình bày Modal trong phát triển di động: khái niệm, các loại màn hình

Tác giả: IT Sectr Đã đăng: 2026-06-09 Thời gian đọc: 5 phút

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à kỹ thuật hiển thị màn hình chặn tương tác với nội dung trước đó cho đến khi cửa sổ modal được đóng.
  • iOS sử dụng UIModalPresentationStyle với các biến thể .fullScreen, .pageSheet, .formSheet và .automatic.
  • Android thực hiện tính modal thông qua DialogFragment, BottomSheetDialogFragment và Activity với cờ Intent.
  • SwiftUI cung cấp các bộ sửa đổi .sheet và .fullScreenCover cho trình bày modal khai báo.
  • Jetpack Compose sử dụng Dialog và ModalBottomSheet để tạo giao diện modal.

Trình bày Modal là gì?

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ểmTrình bày ModalPush Presentation
Chặn quay lạiCó, yêu cầu hành động rõ ràngKhông, nút quay lại luôn có sẵn
Sử dụng điển hìnhBiểu mẫu, ủy quyền, lựa chọnXem chi tiết, điều hướng
Hoạt ảnhTừ dưới lên (iOS), trượt (Android)Từ phải sang trái (iOS)
Ngăn xếp điều hướngKhô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.

Trình bày Modal trong iOS: UIKit và SwiftUI

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.

UIViewController.present

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ự.

SwiftUI .sheet

Cách tiếp cận khai báo của SwiftUI sử dụng các bộ sửa đổi .sheet.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.

swift
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() }
    }
}

Trình bày Modal trong Android: Fragment và Compose

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

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).

Bottom Sheet Modal

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.

kotlin
@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ác thực hành tốt nhất về trình bày modal

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.

Khi nào sử dụng trình bày modal

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.

Những điều cần tránh

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.

Ví dụ mã Swift và Kotlin

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.

swift
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")
    }
}
kotlin
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

Khi nào tôi nên sử dụng Trình bày Modal thay vì Push?

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.

Tôi nên chọn kiểu trình bày modal nào trong iOS?

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.

Làm thế nào để triển khai cửa sổ modal trong Jetpack Compose?

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.

Có thể lồng các cửa sổ modal vào nhau không?

Apple HIGMaterial 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.

Làm thế nào để xử lý việc đóng cửa sổ modal bị mất dữ liệu?

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

  • Trình bày Modal là mẫu điều hướng chặn tương tác với nội dung cha cho đến khi hoàn thành tác vụ.
  • iOS hỗ trợ .fullScreen, .pageSheet, .formSheet qua UIModalPresentationStyle và .sheet trong SwiftUI.
  • Android thực hiện tính modal qua DialogFragment, BottomSheet và Dialog trong Jetpack Compose.
  • Modal khác Push ở chỗ chặn điều hướng quay lại và yêu cầu hành động rõ ràng để đóng.
  • Cửa sổ modal không nên vượt quá 20% kịch bản điều hướng và không nên lồng nhau.
  • Mất dữ liệu khi đóng cửa sổ modal nên được ngăn chặn thông qua ủy quyền và hộp thoại xác nhận.
  • Khuyên nên chọn Page Sheet trong iOS và DialogFragment trong Android cho các tác vụ modal tiêu chuẩn.

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.

Thảo luận dự án

Đọc thêm