How to set up a Base Fragment Class with ViewBinding and ViewModel on Android
Tags: android, kotlin, programming
Published: 2021-04-07 07:32:54

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

```kotlin
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()
    }
}
```

`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.

```kotlin

@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
    }

}

```
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.