Thiết kế ứng dụng thường rườm rà và thường có whiteboard với mũi tên chỉ từ điểm này sang điểm khác.
App bạn ban đầu nghĩ chỉ 1-2 activity, bỗng có multiple flow, fragment và nhiều user interaction. Không hay nếu lấy thứ trên whiteboard replicate dễ dàng vào code?
Chào đón Navigation Component.
Đối với người lạ, Navigation Component không phải UI class khác đặt thay layout cho activity/fragment. Hãy coi nó như bản đồ: thay vì lục địa, bạn có fragment, và cần directions để đi từ lục địa này sang lục địa khác. Nó trình bày fragment và kết nối giữa chúng theo cách top-down. Bài này đi qua khía cạnh chính component này và học cách tích hợp vào app.
Sẵn sàng khởi hành? ⛵️
Học cách dùng
Navigation component có từ Android Studio 3.3 trở lên. Để dùng, thêm dependencies sau vào project:
android { ... } dependencies { implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0' implementation 'androidx.navigation:navigation-ui-ktx:2.0.0' }Để có gì làm việc, tưởng tượng app thiết kế với cấu trúc:
- Start Fragment
- Fragment A
- Fragment B
User có thể đi Fragment A hoặc Fragment B từ Start fragment.

Ứng dụng của chúng ta
Nếu làm không dùng Navigation Component, phải thêm code quen thuộc mở fragment khi button click.
val myFragment : MyFragment = MyFragment() supportFragmentManager.beginTransaction().add(R.id.container, myFragment).commit()Ví dụ nhỏ này chỉ vài dòng ngắn, đơn giản, nhưng chúng ta đồng ý cách này không scale nếu app lớn và user flow phức tạp.
All Aboard
Để dùng Navigation Component, tạo navigation graph. Graph này làm map, outline user flow trong app. Tạo: right-click res folder → new resource file. Đặt tên: user_flow_graph.xml. Chọn type: Navigation.

Tạo resource file mới
Mỗi chuyến đi có home base, chúng ta cũng vậy. Home gọi là NavHost. Placeholder cho destination swap khi user tương tác UI. Thêm NavHost vào main layout activity:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android" xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="https://schemas.android.com/apk/res-auto" tools:context=".MainActivity"> <fragment android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:defaultNavHost="true" app:navGraph="@navigation/user_flow_graph" /> </androidx.constraintlayout.widget.ConstraintLayout>Thêm fragment element chứa nơi fragment hiển thị và swap. Chú ý attribute navGraph link đến XML file trước.
Cần thêm starting destination, app không compile nếu không có.
Mở user_flow_graph.xml, click icon plus nhỏ trong Navigation Editor:

Thêm Destination
Menu popup cho phép tạo placeholder (điền sau) hoặc chọn fragment có sẵn:

Lựa chọn Destination
User flow bắt đầu từ Start Fragment, chọn nó trước.

Home base (chú ý icon ? nhỏ)
Thêm Fragment A và Fragment B.

Tất cả destination
Kết nối 2 destination: click dot hiện ra khi hover destination, kéo đến destination khác.

Khá gọn gàng, phải không?
Chúng ta vừa tạo giữa Start fragment và Fragment A, B là actions.
Shiver me Timbers
Bạn có thể tự hỏi: chỉ connect destination xong việc, mọi thứ tự động work?
Không.
Cần báo code navigate đến destination. Làm sao? Có chút magic.
Đầu tiên, thêm gradle plugin Safe Args. Đảm bảo type safety khi navigate giữa destination.
buildscript { /... } dependencies { ... classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.0.0" } }Cũng cần thêm plugin vào app build.gradle:
apply plugin: "androidx.navigation.safeargs.kotlin" Đảm bảo android.useAndroidX=true trong gradle.properties.
Trước khi tiến xa, hiểu tại sao cần config này. Khi tạo actions, behind the scenes Android Studio sinh code dùng để activate action. Code gồm method và class represent mỗi action. Lấy Start fragment ví dụ. Code sinh cho actions khai báo có class StartFragmentDirections. Method cho class này represent actions tạo trước. Cho cả 2 fragment:
- StartFragmentDirections.actionStartFragmentToFragmentA()
- StartFragmentDirections.actionStartFragmentToFragmentB()
Actions đã translate thành code, dùng chúng:
val action = StartFragmentDirections.actionStartFragmentToFragmentA() Bước cuối: dùng NavController. Object quản lý navigation trong NavHost. Truy cập bằng 3 cách:
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId: Int)
Gộp lại:
fragmentABtn.setOnClickListener { button -> val action = StartFragmentDirections.actionStartFragmentToFragmentA() button.findNavController().navigate(action) }Trim Your Sails
Muốn pass data giữa destination? Giả sử user click item, muốn làm gì đó với item ở destination tiếp theo. Dùng destination arguments. Mở user_flow_graph.xml, click Fragment A. Bên phải, menu attribute Fragment A. Một attribute: Arguments.

Thêm argument: click icon ➕. Popup mở, config argument. Đặt tên, chọn type, thêm default value. Thêm argument String type cho Fragment A, là message từ Start fragment.

Trong Start fragment, nơi define action và gọi generated method, pass argument:
fragmentABtn.setOnClickListener { button -> val action = StartFragmentDirections.actionStartFragmentToFragmentA("Hello From Start Fragment") button.findNavController().navigate(action) }Truy cập ở Fragment A, 2 cách:
- Access bundle lấy message value
class FragmentA: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val bundle = arguments val root = inflater.inflate(R.layout.fragment_a, container, false) val textView : TextView = root.findViewById(R.id.textView) textView.text = bundle?.getString("message") return root } }- Dùng navArgs nếu dùng -ktx dependencies
class FragmentA: Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val args : FragmentAArgs by navArgs() val root = inflater.inflate(R.layout.fragment_a, container, false) val textView : TextView = root.findViewById(R.id.textView) textView.text = args.message return root } }✋ Khi dùng navArgs, cần thêm support Java 8 trong build.gradle.
Tất cả code ở đây tại GitHub repository này.