Android Jetpack Navigation: Load Activity/Fragment With SafeArgs and Parcelable (Kotlin)

April 19, 2020

NOTE: If you have yet to setup jetpack navigation, refer to Android Setup Jetpack Navigation (Kotlin).

Setup

NOTE: Refer https://developer.android.com/jetpack/androidx/releases/navigation

Edit top level / project build.gradle

buildscript {
    ext {
        nav_version = '2.3.0-alpha05'
    }
    repositories {
        google()
        // ...
    }
    dependencies {
        // ...
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
    }
}

Edit project / module build.gradle

apply plugin: "androidx.navigation.safeargs.kotlin"

dependencies {
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
}

Create Fragment

class BatchFragment : Fragment() {
    // private val viewModel: BatchViewModel by viewModels()
    val args: BatchFragmentArgs by navArgs()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.batch, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        if (args.default.isNew) {
            textView.text = "New Batch"
        }
        else {
            textView.text = "Batch Id = ${args.default.id}"
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.sub.BatchFragment">

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Hello" />

</FrameLayout>

Create BatchArgs Class as Args

@Parcelize
class BatchArgs(val id: String? = null): Parcelable {
    val isNew: Boolean
        get() = id == null
}

NOTE: I prefer to use Parcelable as arguments as I don’t have to edit navigation graph for any modification, and it support more complex structure.

Edit Navigagation Graph

Click New Destination icon, select fragment.

  • Id: batchNav
  • Label: Batch

At Arguments -> Add Component

  • name: default
  • type: Custom Parcelable -> Select BatchArgs

Click Add

Drag a path from TestFragment to BatchFragment.

  • id: action_testNav_to_batchNav
  • destination: batchNav
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mobile_navigation"
    app:startDestination="@+id/testNav">

    <fragment
        android:id="@+id/testNav"
        android:name="com.luasoftware.garden.view.TestFragment"
        android:label="@string/title_test"
        tools:layout="@layout/test" >
        <action
            android:id="@+id/action_testNav_to_batchNav"
            app:destination="@id/batchNav" />
    </fragment>
    <fragment
        android:id="@+id/batchNav"
        android:name="com.luasoftware.garden.view.sub.BatchFragment"
        android:label="Batch"
        tools:layout="@layout/batch" >
        <argument
            android:name="default"
            app:argType="com.luasoftware.garden.model.args.BatchArgs" />
    </fragment>
</navigation>
class TestFragment : Fragment() {
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.test, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        testButton.setOnClickListener {
            val args = BatchArgs(id = "abc")
            val action = TestFragmentDirections.actionTestNavToBatchNav(args)
            findNavController().navigate(action)
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".view.TestFragment">

    <Button
        android:id="@+id/testButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Test"
         />

</LinearLayout>
This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.