Skip to content

Instantly share code, notes, and snippets.

@RubyLichtenstein
Last active February 11, 2021 00:40
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RubyLichtenstein/6bb8e8f6cb78ace34475d9e394adda14 to your computer and use it in GitHub Desktop.
Save RubyLichtenstein/6bb8e8f6cb78ace34475d9e394adda14 to your computer and use it in GitHub Desktop.
Painless android fragments with Kotlin

Starting new fragments with Kotlin is easy

Example

val user = User(id = "id", name = "Ruby")
val userFragment: UserFragment = newFragment<User, UserFragment>(user)

How this magic works

1. Define BaseFragment with ARG Parcelable type parameter.

abstract class BaseFragment<ARG : Parcelable> : Fragment() {
    //...
}

1.1. Inside BaseFragment onCreate call getParcelable(FRAGMENT_ARG).

const val FRAGMENT_ARG = "FRAGMENT_ARG"

abstract class BaseFragment<ARG : Parcelable> : Fragment() {
    protected lateinit var arg: ARG

    @CallSuper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            arg = it.getParcelable(FRAGMENT_ARG)!!
        }
    }
}

2. Define newFragment inline function with 2 type parameters

1. ARG - Parcelable argument type

2. reified T your fragment type

inline fun <ARG : Parcelable, reified T : BaseFragment<ARG>> newFragment(arg: ARG? = null): T {
   //...
}

2.1. Put ARG as fragment argument with bundle.putParcelable(FRAGMENT_ARG, arg)

const val FRAGMENT_ARG = "FRAGMENT_ARG"

inline fun <ARG : Parcelable, reified T : BaseFragment<ARG>> newFragment(arg: ARG? = null): T {
    val fragment = T::class.java.newInstance()
    val bundle = Bundle()
    bundle.putParcelable(FRAGMENT_ARG, arg)
    fragment.arguments = bundle
    return fragment
}

3. Create Parcelable fragment argument (Kotlin Android Extensions)

@Parcelize
data class User(val id: String, val name: String) : Parcelable

3.1. Inherit from BaseFragment and fill the type parameter with your Parcelable argumnet

class UserFragment : BaseFragment<User>() {

}

4. Create new fragment with newFragment function and pass the Parcelable argument

val user = User("id", "Ruby")
val userFragment = newFragment<UserFragment>(user)

3.2 In your fragment you can use arg

class UserFragment : BaseFragment<User>() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val user = arg
        showUser(user)
    }

    private fun showUser(user: User) {

    }
}

6. Enjoy and star :)

@Zhuinden
Copy link

Zhuinden commented Jan 9, 2019

If I have no arguments, then this will crash at the !!, no?

@xstar97
Copy link

xstar97 commented Jan 10, 2019

If I have no arguments, then this will crash at the !!, no?

Yes, thats why use "?" Instead, also add an error/ blank case so it doesn't crash...

@prithivraj
Copy link

Looks cool!
How is this approach better than safeargs?

@amirulzin
Copy link

You can check if the FRAGMENT_ARG exist via Bundle.containsKey hence allowing nullable parameters as needed.

@ozbek
Copy link

ozbek commented Jan 10, 2019

How to correctly get an instance of app context?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment