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
staticCompositionLocalOfnhưng quên bao bọcNavHosttrong 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ủaMainActivity, 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ốierror(), 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.ktvà đảm bảo rằngCompositionLocalProviderbao bọc toàn bộ navigation graph của bạn. Sử dụngstaticCompositionLocalOflà 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
startDestinationban đầ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ếuLocalNavController.currenthoạ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ụngCompositionLocal, hãy ưu tiên dùngstaticCompositionLocalOfchoNavController. Khác vớicompositionLocalOfthông thường, phiên bản static không theo dõi từng consumer riêng lẻ. Vì instanceNavControllercủ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.

