How to Add a Fragment the Kotlin way

Image Source: Kotlin on Android

Originally posted as an answer to a StackOverflow question.

Before going to the Kotlin way, let’s recap how we used to add a Fragment in Java.

The naive way:

FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(frameId, fragment);
transaction.commit();

To avoid repeating this boilerplate and error-prone code in every Activity, we would have written a static util method in a class named ActivityUtil.

public static void addFragmentToActivity(FragmentManager manager, Fragment fragment, int frameId) {

FragmentTransaction transaction = manager.beginTransaction();
transaction.add(frameId, fragment);
transaction.commit();

}

And we call this util method from Activity like this:

ActivityUtil.addFragmentToActivity(
getSupportFragmentManager(), fragment, R.id.frag_container);

That’s all, nothing interesting here. Now, let’s see how we can do this better in Kotlin in two steps.

1. Eliminating beginTransaction() and commit()

How many minutes, if not hours, have you wasted debugging your application only to find out that you had missed calling commit() at the end of your Fragment transaction?

To eliminate that boilerplate code, let’s write an Extension function to the FragmentManager class which accepts a Lambda with Receiver as its argument.

Quick intro to the Kotlin features we are about to use:
Extension function is a way to add new functions (or properties) to an existing class even if the class is from a library or SDK. Inside the function we can access the public functions and properties of the class without any qualifiers as though this function is inside the class itself. (Note: Technically it’s not modifying the existing class but creating a static functions under the hood)
Higher Order Function is a function that takes functions as parameters, or returns a function. We can pass a function or return a function from a function like data.
Lambda with Receiver (Function Literals with Receiver) is the combination of above two — an Higher order function that takes an Extension function to a Receiver as its parameter. So in the lambda expression we pass as the argument, we can access the functions and properties of the Receiver as though the lambda function is inside the Receiver itself.

This is the Extension function to the FragmentManager which accepts a Lambda with Receiver as its argument, whereas the FragmentTransaction is the Receiver.

inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> Unit) {
val fragmentTransaction = beginTransaction()
fragmentTransaction.func()
fragmentTransaction.commit()
}

Here the parameter func is an Extension function to the FragmentTransaction and it has zero parameters and returns Unit. We invoke that function after calling the beginTransaction() and end the transaction by calling commit().

Now to add a Fragment, we can call like this from any Activity:

supportFragmentManager.inTransaction {
add(R.id.frameLayoutContent, fragment)
}

Note that inside the lambda we can call the functions of the FragmentTransaction — like add or remove — without any additional qualifiers because this is an Extension function to the FragmentTransaction itself.

Using the above Extension function, we don’t have to call beginTransaction() and commit() every time we add or replace a Fragment now. We can even call multiple operations inside the inTransaction block and everything runs inside a Fragment transaction:

supportFragmentManager.inTransaction {
remove(fragmentA)
add(R.id.frameLayoutContent, fragmentB)
}

Update on 24th Aug, 2017: Dai suggested in the comments that we can improve the inTransaction function even more by making the lambda expression to return the FragmentTransaction:

inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commit()
}

2. Extension functions replaces ActivityUtil

Next, let’s see how the Extension functions to the AppCompatActivity is better than the Util class ActivityUtil.

We can write the following Extension functions to the AppCompatActivity — addFragment and replaceFragment:

fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int){
supportFragmentManager.inTransaction { add(frameId, fragment) }
}


fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) {
supportFragmentManager.inTransaction{replace(frameId, fragment)}
}

Since these are Extension functions to the AppCompatActivity itself, we can access the supportFragmentManager directly inside the function.

Using the above extension functions, now we can add or replace aFragment from any Activity in a single line, without any additional qualifiers:

addFragment(fragment, R.id.fragment_container)

replaceFragment(fragment, R.id.fragment_container)

There is no extra qualifier to call these functions and so this looks as though the Android SDK itself provides these functions.

Using Kotlin, we have fixed the ugly Android API and created a concise, more readable, and less error-prone code to add a Fragment.

This is not the end; these — Extension function, Higher order function, and Lambda with Receiver — are the building blocks of Kotlin to create a nice DSL for your project.

Complement this with my latest post on Kotlin’s Nothing-ness: Kotlin has Nothing but there is nothing like Nothing in Java

Leave your thoughts in the comments or reach me on Twitter.