DEV Community

Cover image for How to set up a Base Fragment Class with ViewBinding and ViewModel on Android
Enya Emmanuel
Enya Emmanuel

Posted on • Edited on

How to set up a Base Fragment Class with ViewBinding and ViewModel on Android

Hey guys! This is my first article on dev.to, and I am super excited about writing again after a long while. Quickly, let's get into the subject at hand.

Building native Android apps require you have a fair knowledge of object-oriented programming. This forms the basis for this article because we will leverage the concepts of Inheritance to set up a base class. In addition to this, you’ll learn about generics, with practical examples.

One may be curious as to why we would want to add another layer of inheritance to our fragment class? This question is usually common with beginner programmers yet to realize the benefits of inheritance.

What is Inheritance in Object-Oriented Programming (OOP)?

Inheritance is a feature in which a class inherits all the features of another class. The class from which the features are inherited is known as the base class, superclass, or parent class, and the class that inherits the features is known as a derived class, subclass, or child class.

E.g. If Class D extends A, it is inheriting the features of A.

What are Generics?

Generics are simply parameterized types. The idea is to allow type (Integer, String, … etc, and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.

Enough theory, let's get started on creating a base fragment class.

STEP 1:

Create a class BaseFragment that extends a Fragment

abstract class BaseFragment<VBinding : ViewBinding, ViewModel : BaseViewModel> : Fragment() {

    open var useSharedViewModel: Boolean = false

    protected lateinit var viewModel: ViewModel
    protected abstract fun getViewModelClass(): Class<ViewModel>

    protected lateinit var binding: VBinding
    protected abstract fun getViewBinding(): VBinding

    private val disposableContainer = CompositeDisposable()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        init()
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setUpViews()
        observeData()
    }

    open fun setUpViews() {}

    open fun observeView() {}

    open fun observeData() {}

    private fun init() {
        binding = getViewBinding()
        viewModel = if (useSharedViewModel) {
            ViewModelProvider(requireActivity()).get(
                getViewModelClass()
            )
        } else {
            ViewModelProvider(this).get(getViewModelClass())
        }
    }

    fun Disposable.addToContainer() = disposableContainer.add(this)

    override fun onDestroyView() {
        disposableContainer.clear()
        super.onDestroyView()
    }
}
Enter fullscreen mode Exit fullscreen mode

abstract class BaseFragment<VBinding : ViewBinding, ViewModel : BaseViewModel> : Fragment()

Basically what we've done here is create a BaseFragment which accepts two types, that is a ViewBinding and a ViewModel. With this class, we move common logic and set up to one place, thereby reducing duplicate codes (boilerplates).

The ViewModel class and the ViewBinding object specified as fields in the base class will be provided by the subclass when getViewModelClass() and getViewBinding() are invoked, respectively.

open fun setUpViews() {} and open fun observeData() {} are methods with a default empty implementation. This makes it optional for its sub-classes to override. The subclasses are not forced to override these methods.

STEP 2:

With the base class set up, we'll use it by creating another fragment class that extends BaseFragment.


@AndroidEntryPoint
class UserListFragment : BaseFragment<FragmentUserListBinding, UserViewModel>() {

    override var useSharedViewModel = true

    override fun getViewModelClass() = UserViewModel::class.java

    override fun getViewBinding() = FragmentUserListBinding.inflate(layoutInflater)

    override fun setUpViews() {

        // set up recycler view and bind data to UI
    }

}

Enter fullscreen mode Exit fullscreen mode

From the code block above, we have been able to achieve a simple and readable fragment class, using the concepts of inheritance and generics.

In summary,

  1. Using a base fragment helps you avoid code and pattern repetition.
  2. You achieve a clean and readable code with the concepts discussed in this article.

That is it for this article. Please share your thoughts on this subject.

Top comments (9)

Collapse
 
sebastianseno profile image
Sebstian Seno • Edited

Halo Enya, Nice article! But when I try your code for my base fragment, why my application crash when I try to go back to previous fragment ?

the error said 'Fragment no longer exists' did you have the same issue ?

hope you will respond my message. Thanks in advance

Collapse
 
enyason profile image
Enya Emmanuel

Hello @sebastianseno thank you. Could You share the link to your project on github. So i check it out. Thats if this sits well with you.

Collapse
 
matiasdelbel profile image
Mati Del Bel

Hey! Nice article! One thing, shouldn't you clear the binding instance on onDestroyView?

Collapse
 
enyason profile image
Enya Emmanuel

Yeah Mati. I did not take that into consideration because I was only trying to demonstrate the use of base fragment. However, for the sake of best practices, I will update the code accordingly. Thanks.

Collapse
 
dishamhooda profile image
dishaMhooda

Hello Enya,

I want to learn this type of architecture , can you please help me from where can I learn this type of architecture or best article link which explains about base classes and its working.

Thank you,
Disha

Collapse
 
msfjarvis profile image
Harsh Shandilya

Great first article Enya! Looking forward to seeing more of what you write :D

Collapse
 
enyason profile image
Enya Emmanuel

Thanks Harsh... Sure you will 😊

Collapse
 
sarimahmed93 profile image
Sarim-Ahmed93 • Edited

Really helpful article. Can you please share your BaseViewModel class too?

Collapse
 
enyason profile image
Enya Emmanuel

Hey @sarimahmed93 I'm glad you learnt a thing form it. Here's the BaseViewmodel from an old project i worked on. Nothing really fancy. Just extending ViewModel.

abstract class BaseViewModel(private val lifeCycleReceiver: LifecycleReceiver = LifecycleReceiverImpl()) :
    ViewModel(), LifecycleReceiver by lifeCycleReceiver {

    private val compositeContainer = CompositeDisposable()

    var mainNavigator: MainNavigator? = null

    override fun onCleared() {
        super.onCleared()
        compositeContainer.clear()
    }

    fun Disposable.addToContainer() = compositeContainer.add(this)
}
Enter fullscreen mode Exit fullscreen mode