Khắc phục lỗi Crash "LocalNavController Not Provided" trong Jetpack Compose

intermediate📱 Android2026-07-06| Android Studio, Jetpack Compose, Navigation Component (androidx.navigation:navigation-compose)

Error Message

java.lang.IllegalStateException: CompositionLocal LocalNavController not provided
#android#jetpack-compose#kotlin#navigation

Sự cốỨng dụng của bạn build hoàn toàn bình thường. Gradle sync báo xanh. Nhưng ngay khi bạn nhấn vào một nút để chuyển màn hình, ứng dụng đột ngột thoát. Kiểm tra Logcat, bạn sẽ thấy một loạt dòng chữ đỏ: java.lang.IllegalStateException: CompositionLocal LocalNavController not provided.

Lỗi này tương tự như một phiên bản NullPointerException trong Jetpack Compose. Nó xảy ra khi một Composable cố gắng lấy NavController từ môi trường (environment) nhưng không tìm thấy gì. Về cơ bản, bạn đang yêu cầu một công cụ chưa được đưa vào hộp dụng cụ.

Tóm tắt: Cách sửa nhanh trong 30 giâyNếu bạn sử dụng LocalNavController tùy chỉnh, bạn phải bao bọc UI của mình trong một CompositionLocalProvider. Việc này thường được thực hiện ở gốc của MainActivity.

val navController = rememberNavController()

CompositionLocalProvider(LocalNavController provides navController) {
    // Nội dung ứng dụng của bạn
    MainScreen()
}

Tại sao lỗi này xảy raHãy coi CompositionLocal là một cách để truyền dữ liệu xuống cây UI một cách thầm lặng. Nó giúp bạn tránh tình trạng "prop-drilling" - quá trình tẻ nhạt khi phải truyền navController qua hàng chục tham số hàm chỉ để đến được một cái nút.

Lỗi crash xảy ra khi LocalNavController.current được gọi nhưng hệ thống phân cấp cha thiếu provider. Điều này thường xuất phát từ ba tình huống cụ thể:

  • Thiếu Root: Bạn đã định nghĩa staticCompositionLocalOf nhưng quên bao bọc NavHost trong provider.- Preview bị cô lập: Android Studio Previews chạy trong một môi trường độc lập. Chúng không biết đến sự tồn tại của MainActivity, vì vậy không thể thấy controller mà bạn đã định nghĩa ở đó.- Lỗi định nghĩa thủ công: Bạn có thể đã khởi tạo local với một khối error(), khối này sẽ kích hoạt vì không có giá trị mặc định nào được cung cấp.## Giải pháp từng bước### 1. Thiết lập Root toàn cụcHầu hết các lập trình viên gặp phải lỗi này do thiếu provider ở cấp cao nhất. Hãy mở MainActivity.kt và đảm bảo rằng CompositionLocalProvider bao bọc toàn bộ navigation graph của bạn. Sử dụng staticCompositionLocalOf là tốt nhất ở đây vì instance của controller hiếm khi thay đổi trong suốt vòng đời ứng dụng.
// 1. Định nghĩa local (thường trong file NavGraph.kt)
val LocalNavController = staticCompositionLocalOf<NavHostController> {
    error("Không tìm thấy NavController!")
}

// 2. Bao bọc nội dung của bạn
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState) {
            setContent {
                val navController = rememberNavController()
                CompositionLocalProvider(LocalNavController provides navController) {
                    AppNavigation() 
                }
            }
        }
    }
}

2. Sửa lỗi "Render Problem" trong PreviewNếu ứng dụng của bạn chạy tốt trên thiết bị vật lý nhưng tab Preview lại hiển thị một hộp màu xám, bạn cần một mock provider. Preview cần ngữ cảnh local riêng của chúng. Vì bạn không thực sự điều hướng giữa các màn hình trong một preview tĩnh, chỉ cần rememberNavController() là đủ để thỏa mãn trình biên dịch.

@Preview(showBackground = true)
@Composable
fun MyScreenPreview() {
    val mockController = rememberNavController()
    CompositionLocalProvider(LocalNavController provides mockController) {
        MyScreen()
    }
}

3. Giới hạn phạm vi trong NavHostNếu bạn không muốn biến controller thành toàn cục, bạn có thể chỉ bao bọc NavHost. Tuy nhiên, hãy cẩn thận. Bất kỳ Composable nào gọi LocalNavController.current bên ngoài khối cụ thể này vẫn sẽ khiến ứng dụng bị crash ngay lập tức.

Danh sách kiểm tra xác minhĐừng vội kết luận là đã sửa xong chỉ vì màn hình đầu tiên tải được. Hãy thực hiện ba bước kiểm tra sau:

  • Khởi động nguội (Cold Start): Đóng ứng dụng hoàn toàn và khởi động lại. Đảm bảo startDestination ban đầu tải mượt mà.- Kiểm tra 3 cấp độ: Điều hướng sâu ít nhất ba màn hình trong UI của bạn. Nếu LocalNavController.current hoạt động ở cấp sâu nhất, hệ thống phân cấp của bạn đã được cấu hình đúng.- Hiển thị Preview: Kiểm tra chế độ Design trong Android Studio. Nếu lớp phủ "Render Problem" biến mất, mock provider của bạn đang hoạt động tốt.## Mẹo chuyên nghiệp: Vấn đề hiệu năngKhi sử dụng CompositionLocal, hãy ưu tiên dùng staticCompositionLocalOf cho NavController. Khác với compositionLocalOf thông thường, phiên bản static không theo dõi từng consumer riêng lẻ. Vì instance NavController của bạn thường chỉ được tạo một lần và không bao giờ thay đổi, phiên bản static sẽ giúp giảm bớt chi phí recomposition không cần thiết trên toàn bộ ứng dụng.

Related Error Notes