<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Loveth Nwokike</title>
    <description>The latest articles on DEV Community by Loveth Nwokike (@kulloveth).</description>
    <link>https://dev.to/kulloveth</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F76354%2F7daa25a3-d877-4e51-bcde-e84bd90c9a1b.jpg</url>
      <title>DEV Community: Loveth Nwokike</title>
      <link>https://dev.to/kulloveth</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kulloveth"/>
    <language>en</language>
    <item>
      <title>Firebase User Authentication:Email and Google Sign in on Android with Kotlin</title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Mon, 11 Jan 2021 22:10:08 +0000</pubDate>
      <link>https://dev.to/kulloveth/firebase-user-authentication-email-and-google-sign-in-on-android-with-kotlin-goa</link>
      <guid>https://dev.to/kulloveth/firebase-user-authentication-email-and-google-sign-in-on-android-with-kotlin-goa</guid>
      <description>&lt;p&gt;Firebase authentication is a fast and easy way of providing secure user verification process. There are several platforms made available with firebase to simplify the process including Facebook, phone auth, GitHub, google, email, twitter and more. We will be discussing google and email implementation in this post.&lt;/p&gt;

&lt;h3&gt;
  
  
  By the end of this post you will learn how to :
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Create and setup a firebase account&lt;/li&gt;
&lt;li&gt;Add your app to firebase &lt;/li&gt;
&lt;li&gt;Enable google and email authentication with firebase&lt;/li&gt;
&lt;li&gt;Implement google authentication&lt;/li&gt;
&lt;li&gt;Implement Email Authentication&lt;/li&gt;
&lt;li&gt;Validate user details and check for errors&lt;/li&gt;
&lt;li&gt;Verify users and recover passwords&lt;/li&gt;
&lt;li&gt;🎁 Save user to cloud firestore&lt;/li&gt;
&lt;li&gt;Fetch signed in user&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What you should already know
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;familiarity with writing android apps with Kotlin&lt;/li&gt;
&lt;li&gt;dependency injection(di) with hilt but not very necessary&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; di is the short for dependency injection and will be used through out the post. &lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kulloveth" rel="noopener noreferrer"&gt;
        kulloveth
      &lt;/a&gt; / &lt;a href="https://github.com/kulloveth/FirebaseAndroidAuthSample" rel="noopener noreferrer"&gt;
        FirebaseAndroidAuthSample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sample simplifying firebase authentication with email and gmail in android with Kotlin
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;FirebaseAndroidAuthSample&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A sample simplifying firebase authentication with email and google in android&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
   &lt;td&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/signin.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fsignin.png"&gt;&lt;/a&gt;
   &lt;/td&gt;
   &lt;td&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/signup.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fsignup.png"&gt;&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/pasworddialog.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fpasworddialog.png"&gt;&lt;/a&gt;
   &lt;/td&gt;
   &lt;td&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/passworddialog.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fpassworddialog.gif"&gt;&lt;/a&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Libraries used&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Firebase&lt;/li&gt;
&lt;li&gt;Hilt-Android&lt;/li&gt;
&lt;li&gt;Navigation Components&lt;/li&gt;
&lt;li&gt;ViewModel&lt;/li&gt;
&lt;li&gt;LiveData&lt;/li&gt;
&lt;li&gt;ViewBinding&lt;/li&gt;
&lt;li&gt;Timber&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;MIT License&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;Copyright (c) 2021  Loveth Nwokike
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kulloveth/FirebaseAndroidAuthSample" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;To create a firebase account you must have a google account which you will be using to sign in. Go to &lt;a href="https://firebase.google.com" rel="noopener noreferrer"&gt;firebase&lt;/a&gt; and click on sign at the top right corner of the page then proceed to use your preferred google account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flwycad5gdudvbqghuxxe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flwycad5gdudvbqghuxxe.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next is to register/add your app to it. There are two ways you can do that either by using the firebase console following these &lt;a href="https://firebase.google.com/docs/android/setup?authuser=0#console" rel="noopener noreferrer"&gt;steps&lt;/a&gt; or the easier way using the firebase assistant in android studio&lt;/p&gt;

&lt;h3&gt;
  
  
  To add your app to firebase using the firebase assistant in android studio:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Go to tools &amp;gt; firebase &amp;gt; authentication &amp;gt; email and password    &amp;gt;connect to firebase&lt;/li&gt;
&lt;li&gt;You will be taken to the browser where you can
either choose an existing app or create a new one. If everything is successful you get a popup like this:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsgrilosz790gh7a106kd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsgrilosz790gh7a106kd.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Then you click on connect and finally you get this&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgpd42x0iywbwo54fere7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgpd42x0iywbwo54fere7.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;confirming that your app has been connected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;back in android studio, In the firebase assistant click on         &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Authentication &amp;gt; email and password authentication &amp;gt;Add firebase authentication to your app&lt;/li&gt;
&lt;li&gt; Firestore &amp;gt; Read and write documents with Cloud Firestore &amp;gt; Add Cloud Firestore to your app 
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;then allow the app to sync&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point a &lt;code&gt;googleservices.json&lt;/code&gt; has been added to your app directory and some dependencies added as well. When you check your app level &lt;code&gt;build.gradle&lt;/code&gt; you will find out that the following dependency have been added for you&lt;br&gt;
&lt;code&gt;implementation 'com.google.firebase:firebase-auth:20.0.1'&lt;/code&gt;&lt;br&gt;
&lt;code&gt;implementation 'com.google.firebase:firebase-firestore:22.0.1'&lt;/code&gt;&lt;br&gt;
and google service plugin added as well&lt;br&gt;
&lt;code&gt;id 'com.google.gms.google-services'&lt;/code&gt;&lt;br&gt;
also in project level build.gradle google service class path has been added&lt;br&gt;
 &lt;code&gt;classpath 'com.google.gms:google-services:4.3.4'&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Authentication by  email
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Back in firebase console enable email by
&lt;ol&gt;
&lt;li&gt;Going to &lt;a href="https://firebase.google.com/?authuser=0" rel="noopener noreferrer"&gt;firebase&lt;/a&gt;, Click on console&lt;/li&gt;
&lt;li&gt;Select your app&lt;/li&gt;
&lt;li&gt;by the left hand menu select authentication&lt;/li&gt;
&lt;li&gt;click on sign in method and enable email/password&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjaa2p0r40cjilw6gv3d4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjaa2p0r40cjilw6gv3d4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a di class to initalize firebaseAuth and firestore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FirebaseModule.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Module
@InstallIn(ApplicationComponent::class)
class FireBaseModule {

    @Provides
    @Singleton
    fun provideFireBaseAuth(): FirebaseAuth {
        return FirebaseAuth.getInstance()
    }
 @Provides
    @Singleton
    fun provideFirestore()= FirebaseFirestore.getInstance()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create a class for firebaseSource&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;FirebaseSource.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class FireBaseSource @Inject constructor(private val firebaseAuth: FirebaseAuth,private val firestore: FirebaseFirestore) {

fun signUpUser(email:String,password:String,fullName:String) = firebaseAuth.createUserWithEmailAndPassword(email,password)

fun signInUser(email: String,password: String) = firebaseAuth.signInWithEmailAndPassword(email,password)

fun saveUser(email: String,name:String)=firestore.collection("users").document(email).set(User(email = email,fullName = name))
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;here we have methods to sign up, sign and save a user&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;firebaseAuth.createUserWithEmailAndPassword(email,password)&lt;/code&gt;
signs up a user with an email and password&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;firebaseAuth.signInWithEmailAndPassword(email,password)&lt;/code&gt;&lt;br&gt;
signs in user with an existing email and password&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;firestore.collection("users").document(email).set(User(email = email,fullName = name))&lt;/code&gt; creates users document and save a user to it&lt;br&gt;
Repository.kt&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Repository @Inject constructor(private val fireBaseSource: FireBaseSource) {

fun signUpUser(email: String, password: String, fullName: String) = fireBaseSource.signUpUser(email, password, fullName)

fun signInUser(email: String, password: String) = fireBaseSource.signInUser(email, password)

fun saveUser(email: String, name: String) = fireBaseSource.saveUser(email, name)

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;SignUpViewModel.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SignUpViewModel @ViewModelInject constructor(
    private val repository: Repository,
    private val networkControl: NetworkControl,
    private val firebaseAuth: FirebaseAuth
) : ViewModel() {

    private val userLiveData = MutableLiveData&amp;lt;Resource&amp;lt;User&amp;gt;&amp;gt;()
    fun signUpUser(email: String, password: String, fullName: String): LiveData&amp;lt;Resource&amp;lt;User&amp;gt;&amp;gt; {
                when {
                    TextUtils.isEmpty(email) &amp;amp;&amp;amp; TextUtils.isEmpty(password) &amp;amp;&amp;amp; TextUtils.isEmpty( fullName ) -&amp;gt; {
                        userLiveData.postValue(Resource.error(null, "field must not be empty"))
                    }
                    password.length &amp;lt; 8 -&amp;gt; {
                        userLiveData.postValue( Resource.error(null, "password must not be less than 8"))
                    }
                    networkControl.isConnected() -&amp;gt; {
                            userLiveData.postValue(Resource.loading(null))
                        firebaseAuth.fetchSignInMethodsForEmail(email).addOnCompleteListener {
                            if (it.result?.signInMethods?.size == 0) {
                                repository.signUpUser(email, password, fullName).addOnCompleteListener { task -&amp;gt;
                                        if (task.isSuccessful) {
                                            firebaseAuth.currentUser?.sendEmailVerification()
                                            userLiveData.postValue( Resource.success( User( email = email, fullName = fullName )))
                                        } else {
                                            userLiveData.postValue( Resource.error(null, it.exception?.message.toString()))
                                        } }
                            } else {
                                userLiveData.postValue(Resource.error(null, "email already exist"))
                            } }
                    } else -&amp;gt; {
                    userLiveData.postValue(Resource.error(null, "No internet connection"))
            } }
        return userLiveData
   }

  fun saveUser(email: String, name: String) {
        repository.saveUser(email, name).addOnCompleteListener {
            if (it.isSuccessful) {
            _saveUserLiveData.postValue(Resource.success(User(email,name)))
            }else{
                _saveUserLiveData.postValue(Resource.error(null,it.exception?.message.toString()))
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  In the SignupViewModel class we check for errors and validate user details
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;checks if the fields are empty&lt;/li&gt;
&lt;li&gt;checks if the password field is less than 8&lt;/li&gt;
&lt;li&gt;checks if the email already exists &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;then throws up suitable errors for all cases otherwise the user is successfully signed up with their details and email verification is sent to them &lt;code&gt;firebaseAuth.currentUser?.sendEmailVerification()&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a method to save to the user detail to firestore if successfully signed up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SignUpFragment.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;binding?.signUpBtn?.setOnClickListener {
            val emailText = binding?.emailEt?.text?.toString()
            val passwordText =  binding?.passwordEt?.text.toString()
            val fullNameText =  binding?.fullNameEt?.text?.toString()
            viewModel.signUpUser( emailText.toString(), passwordText, fullNameText.toString()).observe(viewLifecycleOwner, {
                        when (it.status) {
                            Status.SUCCESS -&amp;gt; {
viewModel.saveUser( it.data?.email.toString(), it.data?.fullName.toString())
                                view.showsnackBar("User account registered")
                            }
                            Status.ERROR -&amp;gt; {
                                view.showsnackBar(it.message!!)
                            }
                            Status.LOADING -&amp;gt; {
                                view.showsnackBar("...")
                            }
                        } })
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the signup fragment we observe the signup user method, save user if successfully signed up and display message with the snackBar for changes that occur with the success, error and loading cases &lt;/p&gt;
&lt;h3&gt;
  
  
  Gmail sign in
&lt;/h3&gt;

&lt;p&gt;To signup up user with gmail&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable gmail signin from firebase console&lt;/li&gt;
&lt;li&gt;Add required dependency &lt;code&gt;implementation 'com.google.android.gms:play-services-auth:19.0.0'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;add your certificate &lt;a href="https://developers.google.com/android/guides/client-auth?authuser=0" rel="noopener noreferrer"&gt;fingerprint&lt;/a&gt; to your app from project settings in the console&lt;/li&gt;
&lt;li&gt;Ensure the correct package name and fingerprint is registered with the OAuth 2.0 Client IDs part of &lt;a href="https://console.cloud.google.com/apis/credentials?authuser=0&amp;amp;pli=1&amp;amp;project=wellnesscity-efcc6&amp;amp;folder=&amp;amp;organizationId=" rel="noopener noreferrer"&gt;credentials&lt;/a&gt; page of the GCP platform&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In your layout add google button&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;com.google.android.gms.common.SignInButton
            android:id="@+id/google_signIn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/dimens_40dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintTop_toBottomOf="@id/orTv"
            android:layout_marginBottom="@dimen/dimens_100dp"
            android:layout_marginStart="@dimen/standard_padding"
            android:layout_marginEnd="@dimen/standard_padding" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next we configure googlesigninoptions(gso) in the di class calling the requestidToken and requestEmail then create the googleClient object with gso configured&lt;/p&gt;

&lt;p&gt;FirebaseModule.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   @Provides
    @Singleton
    fun provideGso(@ApplicationContext context: Context) = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(context.getString(R.string.default_web_client_id))
        .requestEmail()
        .build()

    @Provides
    @Singleton
    fun provideGoogleClient(@ApplicationContext context: Context, gso:GoogleSignInOptions)= GoogleSignIn.getClient(context, gso)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;FirebaseSource.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun signInWithGoogle(acct: GoogleSignInAccount) = firebaseAuth.signInWithCredential(GoogleAuthProvider.getCredential(acct.idToken,null))

fun fetchUser()=firestore.collection("users").get()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Repository.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; fun signInWithGoogle(acct: GoogleSignInAccount) = fireBaseSource.signInWithGoogle(acct)

fun fetchUser() = fireBaseSource.fetchUser()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  SignIn User with email or gmail
&lt;/h3&gt;

&lt;p&gt;LoginViewModel.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun signInUser(email: String, password: String): LiveData&amp;lt;Resource&amp;lt;User&amp;gt;&amp;gt; {
        when {
            TextUtils.isEmpty(email) &amp;amp;&amp;amp; TextUtils.isEmpty(password) -&amp;gt; {
                userLiveData.postValue(Resource.error(null, "Enter email and password"))
            }
            networkControl.isConnected() -&amp;gt; {
                userLiveData.postValue(Resource.loading(null))
                firebaseAuth.fetchSignInMethodsForEmail(email).addOnCompleteListener {
                    if (it.result?.signInMethods?.size == 0) {
                        userLiveData.postValue(Resource.error(null, "Email does not exist"))
                    } else {
                        repository.signInUser(email, password).addOnCompleteListener { task -&amp;gt;
                            if (task.isSuccessful) {
                                firebaseAuth.currentUser?.isEmailVerified?.let { verified -&amp;gt;
                                    if (verified) {
                                        repository.fetchUser().addOnCompleteListener { userTask -&amp;gt;
                                            if (userTask.isSuccessful) {
                                                userTask.result?.documents?.forEach {
                                                    if (it.data!!["email"] == email) {
                                                        val name = it.data?.getValue("fullName")
                                                        userLiveData.postValue(Resource.success(User(firebaseAuth.currentUser?.email!!, name?.toString()!!)
                                                            )) } }
                                            } else {
                                                userLiveData.postValue(Resource.error(null, userTask.exception?.message.toString()))
                                            }
                                        }
                                    } else {
                                        userLiveData.postValue(Resource.error(null, "Email is not verified, check your email"))
                                    } }
                            } else {
                                userLiveData.postValue(Resource.error(null, task.exception?.message.toString()))
                                Timber.e(task.exception.toString())
                            } } } }
            }
            else -&amp;gt; {
                userLiveData.postValue(Resource.error(null, "No internet connection"))
            }
        }
        return userLiveData
    }

 fun signInWithGoogle(acct: GoogleSignInAccount): LiveData&amp;lt;Resource&amp;lt;User&amp;gt;&amp;gt; {
        repository.signInWithGoogle(acct).addOnCompleteListener { task -&amp;gt;
            if (task.isSuccessful) {
                gMailUserLiveData.postValue(
                    Resource.success(
                        User(
                            firebaseAuth.currentUser?.email!!,
                            firebaseAuth.currentUser?.displayName!!
                        )
                    )
                )
            } else {
                gMailUserLiveData.postValue(Resource.error(null, "couldn't sign in user"))
            }

        }
        return gMailUserLiveData
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In LoginViewModel we have signin method for both email and gmail&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;For sign in with email&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We check if email exist&lt;/li&gt;
&lt;li&gt;check if email has been verified&lt;/li&gt;
&lt;li&gt;then check the email in firestore to get the full name associated with it during registration and post it through LiveData for the view to observe on sign in&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For sign in with google&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We get the selected google account&lt;/li&gt;
&lt;li&gt;then get the display name and email associated with it and post via LiveData&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LoginFragment.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@AndroidEntryPoint
class LoginFragment : Fragment() {

    private val viewModel: LoginViewModel by viewModels()
    private var binding: FragmentLoginBinding? = null

    @Inject
    lateinit var googleSignInClient: GoogleSignInClient

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentLoginBinding.inflate(layoutInflater)
        return binding?.root
    }

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



        binding?.signUpTv?.setOnClickListener {

            if (findNavController().currentDestination?.id == R.id.loginFragment) {
                NavHostFragment.findNavController(this)
                    .navigate(LoginFragmentDirections.actionLoginFragmentToSignUpFragment())
            }
        }
        binding?.signInBtn?.setOnClickListener {
            val emailText = binding?.emailEt?.text?.toString()
            val passwordText = binding?.passwordEt?.text.toString()
            viewModel.signInUser(emailText!!, passwordText).observe(viewLifecycleOwner, {
                when (it.status) {
                    Status.LOADING -&amp;gt; {
                        view.showsnackBar("...")
                    }

                    Status.SUCCESS -&amp;gt; {
                        view.showsnackBar("Login successful")
                        if (findNavController().currentDestination?.id == R.id.loginFragment) {
                            NavHostFragment.findNavController(this)
                                .navigate(
                                    LoginFragmentDirections.actionLoginFragmentToDashBoardFragment(
                                        it.data?.fullName!!
                                    )
                                )
                        }
                    }

                    Status.ERROR -&amp;gt; {
                        view.showsnackBar(it.message!!)
                    }
                }
            })
        }

        binding?.googleSignIn?.setOnClickListener {
            signIn()
        }
    }


    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == RC_SIGN_IN) {

            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                val account = task.getResult(ApiException::class.java)

                viewModel.signInWithGoogle(account!!).observe(viewLifecycleOwner, {
                    if (it.status == Status.SUCCESS) {
                        if (findNavController().currentDestination?.id == R.id.loginFragment) {
                            NavHostFragment.findNavController(this).navigate(
                                LoginFragmentDirections.actionLoginFragmentToDashBoardFragment(it?.data?.fullName!!)
                            )
                            // Timber.d("display ${fAuth.currentUser?.displayName} ")
                        }
                    } else if (it.status == Status.ERROR) {
                        requireView().showsnackBar(it.message!!)
                    }
                })
            } catch (e: ApiException) {
                Toast.makeText(requireContext(), e.message, Toast.LENGTH_SHORT).show()
            }
        }
    }

    private fun signIn() {

        val signInIntent: Intent = googleSignInClient.signInIntent

        startActivityForResult(signInIntent, RC_SIGN_IN)

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In the LoginFragment &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We observe the sign in method and get the user details&lt;/li&gt;
&lt;li&gt;We receive the data from the google account in the onActivityResult&lt;/li&gt;
&lt;li&gt;call startActivityForResult for the request code received&lt;/li&gt;
&lt;li&gt;If successful we navigate the user to dashboard activity and display their name&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Forgot password
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In the FirebaseSource.kt we create the reset password method &lt;code&gt;fun sendForgotPassword(email: String) = firebaseAuth.sendPasswordResetEmail(email)&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;In the Repository.kt we call the method &lt;code&gt;fun sendForgotPassword(email: String)=fireBaseSource.sendForgotPassword(email)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In the LoginViewModel we create a LiveData to observe for when the email is sent
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  fun sendResetPassword(email: String): LiveData&amp;lt;Resource&amp;lt;User&amp;gt;&amp;gt; {

        when {
            TextUtils.isEmpty(email) -&amp;gt; {
                sendResetPasswordLiveData.postValue(Resource.error(null, "Enter registered email"))
            }
            networkControl.isConnected() -&amp;gt; {
                repository.sendForgotPassword(email).addOnCompleteListener { task -&amp;gt;
                    sendResetPasswordLiveData.postValue(Resource.loading(null))
                    if (task.isSuccessful) {
                        sendResetPasswordLiveData.postValue(Resource.success(User()))
                    } else {
                        sendResetPasswordLiveData.postValue(
                            Resource.error(
                                null,
                                task.exception?.message.toString()
                            )
                        )
                    }
                }
            }
            else -&amp;gt; {
                sendResetPasswordLiveData.postValue(Resource.error(null, "No internet connection"))
            }
        }
        return sendResetPasswordLiveData
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;LoginFragment.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; val dialog = AlertDialog.Builder(requireContext())
        val inflater = (requireActivity()).layoutInflater
        val v = inflater.inflate(R.layout.forgot_password, null)
        dialog.setView(v)
            .setCancelable(false)
        val d = dialog.create()
        val emailEt = v.findViewById&amp;lt;TextInputEditText&amp;gt;(R.id.emailEt)
        val sendBtn = v.findViewById&amp;lt;MaterialButton&amp;gt;(R.id.sendEmailBtn)
        val dismissBtn = v.findViewById&amp;lt;MaterialButton&amp;gt;(R.id.dismissBtn)


        sendBtn.setOnClickListener {
            viewModel.sendResetPassword(emailEt.text.toString()).observeForever {
                if (it.status == Status.SUCCESS){
                    view.showsnackBar("reset email sent")
                }else{
                    view.showsnackBar(it.message.toString())
                }
            }
        }
        dismissBtn.setOnClickListener {
            d.dismiss()
        }


        binding?.forgotPasswordTv?.setOnClickListener {
            d.show()
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We create a dialog that is displayed when forgot password is clicked on so the user can enter a registered email and received a link to reset their password&lt;/p&gt;

&lt;p&gt;Finally we have a working user authentication to verify and validate user with email/password and google. You can checkout this repository for a complete implementation.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kulloveth" rel="noopener noreferrer"&gt;
        kulloveth
      &lt;/a&gt; / &lt;a href="https://github.com/kulloveth/FirebaseAndroidAuthSample" rel="noopener noreferrer"&gt;
        FirebaseAndroidAuthSample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sample simplifying firebase authentication with email and gmail in android with Kotlin
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;FirebaseAndroidAuthSample&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A sample simplifying firebase authentication with email and google in android&lt;/p&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
  &lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
   &lt;td&gt;
&lt;br&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/signin.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fsignin.png"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;/td&gt;
&lt;br&gt;
   &lt;td&gt;
&lt;br&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/signup.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fsignup.png"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;/td&gt;
&lt;br&gt;
  &lt;/tr&gt;
&lt;br&gt;
    &lt;tr&gt;
&lt;br&gt;
&lt;td&gt;
&lt;br&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/pasworddialog.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fpasworddialog.png"&gt;&lt;/a&gt;&lt;br&gt;
   &lt;/td&gt;
&lt;br&gt;
   &lt;td&gt;
&lt;br&gt;
     &lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/FirebaseAndroidAuthSample/screenshot/passworddialog.gif"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FFirebaseAndroidAuthSample%2Fscreenshot%2Fpassworddialog.gif"&gt;&lt;/a&gt;&lt;br&gt;
    &lt;/td&gt;
&lt;br&gt;
  &lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Libraries used&lt;/h3&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Firebase&lt;/li&gt;
&lt;li&gt;Hilt-Android&lt;/li&gt;
&lt;li&gt;Navigation Components&lt;/li&gt;
&lt;li&gt;ViewModel&lt;/li&gt;
&lt;li&gt;LiveData&lt;/li&gt;
&lt;li&gt;ViewBinding&lt;/li&gt;
&lt;li&gt;Timber&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;MIT License&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;Copyright (c) 2021  Loveth Nwokike
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kulloveth/FirebaseAndroidAuthSample" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>kotlin</category>
      <category>android</category>
      <category>mobile</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Git and Github:Git commands  I frequently use</title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Sun, 06 Dec 2020 14:36:39 +0000</pubDate>
      <link>https://dev.to/kulloveth/git-and-github-git-commands-i-frequently-use-4885</link>
      <guid>https://dev.to/kulloveth/git-and-github-git-commands-i-frequently-use-4885</guid>
      <description>&lt;p&gt;Git commands are very important to everyone that needs to track a set of files using a distributed version control system(vcs).&lt;/p&gt;

&lt;p&gt;In this post I will be listing commands I use while contributing to open source projects, my personal projects or any git related operation.&lt;/p&gt;

&lt;p&gt;To understand distributed vcs better check out this post &lt;a href="https://www.freecodecamp.org/news/git-and-github-overview/"&gt;Git vs Github&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;You have a project, folder, directory containing any kind of file be it source code, document, pictures and you want to keep track of the changes you make to them and possibly share them with the public then this can be very helpful.&lt;/p&gt;

&lt;h4&gt;
  
  
  To add git to a directory
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Open you terminal&lt;/li&gt;
&lt;li&gt;Go into the directory&lt;/li&gt;
&lt;li&gt;Type git init&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that you have added git to the directory you are ready to start tracking the files inside.&lt;/p&gt;

&lt;h4&gt;
  
  
  To add files in the directory to git then type:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;git add . (to add all the files)&lt;/li&gt;
&lt;li&gt;git add &amp;lt;file-name&amp;gt; (to add only a particular file)&lt;/li&gt;
&lt;li&gt;git commit -m "message" (to finally put them in git with a message to help you understand the changes that actually occurred)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every changes you make you have the above step so that git will continue keeping track of it. Next step is to push it to a remote repository so that you can always have a copy for when you need those files but don't have access to your computer or you want to share it with the public.&lt;/p&gt;

&lt;h4&gt;
  
  
  To save your file on gitHub do the following:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Log in to your account on &lt;a href="https://github.com"&gt;github&lt;/a&gt; or create one if you are a new user.&lt;/li&gt;
&lt;li&gt;Click the + icon on the top right corner close to your avatar to create a &lt;a href="https://github.com/new"&gt;new repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Get the url to the repository you just created by clicking on the repository name, go to code which is highlighted in green colour, click on it and then copy the url&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rlf9XDZF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tluzrswc4t3c64h1djmv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rlf9XDZF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tluzrswc4t3c64h1djmv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  In your terminal you type
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;git config --global user.name "gitHub username"&lt;/li&gt;
&lt;li&gt;git config --global user.email "github email"&lt;/li&gt;
&lt;li&gt;git remote add origin &amp;lt;the url you copied&amp;gt;&lt;/li&gt;
&lt;li&gt;git push -u origin master&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your files now have a remote backup and can be accessed from anywhere.&lt;/p&gt;

&lt;h4&gt;
  
  
  If you added a file but you no longer want it to be tracked by git:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;git rm -r --cached &amp;lt;folder name&amp;gt;(if its a folder)&lt;/li&gt;
&lt;li&gt;git rm --cached &amp;lt;file name&amp;gt;(if its a file)&lt;/li&gt;
&lt;li&gt;add the removed file or folder to &lt;code&gt;.gitignore&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;git add .&lt;/li&gt;
&lt;li&gt;git commit -m "message"
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  You have forked a repository not owned by you and want to stay up to date with changes happening in that repository
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;git remote add upstream &lt;/li&gt;
&lt;li&gt;git fetch upstream&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And you can update the branch you are working on from the upstream master by typing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;git rebase upstream/master&lt;/li&gt;
&lt;li&gt;git push -f origin branch(this is a force push because the remote repository might reject the changes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  To check the remote urls  connected to the project you are working on:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;git remote -v (this lists all the repository url both origin and upstreams connected to your project)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Working with branches
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;git checkout -b &amp;lt;branch name&amp;gt;(to create a new branch)&lt;/li&gt;
&lt;li&gt;git checkout &amp;lt;branch name&amp;gt;(to go into an already existing branch)&lt;/li&gt;
&lt;li&gt;git branch (to list all the local branches)&lt;/li&gt;
&lt;li&gt;git branch -d &amp;lt;branch name&amp;gt; (to delete a branch)&lt;/li&gt;
&lt;li&gt;git branch -m &amp;lt;new name&amp;gt; (to rename a branch while in it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will continue updating this post as I work with more git commands or remember any which I must have forgotten I used. You can add commands you have used in the comment section and hope this helps someone.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>git</category>
      <category>github</category>
    </item>
    <item>
      <title>Jetpack Datastore Android: Saving data with preference datastore</title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Fri, 20 Nov 2020 23:27:33 +0000</pubDate>
      <link>https://dev.to/kulloveth/jetpack-datastore-saving-data-on-android-with-preference-datastore-451k</link>
      <guid>https://dev.to/kulloveth/jetpack-datastore-saving-data-on-android-with-preference-datastore-451k</guid>
      <description>&lt;p&gt;Jetpack datastore is a new data storage solution added to the jetpack library and is currently in alpha. It provides two types of implementation: &lt;/p&gt;

&lt;h2&gt;
  
  
  Preference Datastore
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Store and access data using keys&lt;/li&gt;
&lt;li&gt;Stores data asynchronously&lt;/li&gt;
&lt;li&gt;Uses coroutine and flow&lt;/li&gt;
&lt;li&gt;Safe to call on ui thread&lt;/li&gt;
&lt;li&gt;Safe from runtime exception&lt;/li&gt;
&lt;li&gt;Does not provide type-safety&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proto DataStore
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Allows storing data as custom data type&lt;/li&gt;
&lt;li&gt;Provides type-safety&lt;/li&gt;
&lt;li&gt;Stores data asynchronously&lt;/li&gt;
&lt;li&gt;Uses coroutine and flow&lt;/li&gt;
&lt;li&gt;Safe to call on ui thread&lt;/li&gt;
&lt;li&gt;Safe from runtime exception&lt;/li&gt;
&lt;li&gt;Defines schema using protocol buffers&lt;/li&gt;
&lt;li&gt;They are faster, smaller, simpler, and less ambiguous than XML and other similar data formats. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Jetpack datastore is aimed at replacing SharedPreferences. &lt;a href="https://developer.android.com/training/data-storage/shared-preferences" rel="noopener noreferrer"&gt;SharedPreference&lt;/a&gt; contains a method that runs operation on the ui thread which can possibly block it causing an &lt;a href="https://developer.android.com/topic/performance/vitals/anr" rel="noopener noreferrer"&gt;ANR&lt;/a&gt; error but with datastore operations are run asynchronously off the main thread using coroutine and flow. This is the first part of the post and we will be looking at preference datastore. &lt;/p&gt;

&lt;p&gt;We will be using a sample app that includes a page displaying food types depending on selected diet&lt;/p&gt;

&lt;p&gt;To use preference datastore we need to add the dependency&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;implementation "androidx.datastore:datastore-preferences:1.0.0-alpha03"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://developer.android.com/training/dependency-injection/hilt-android" rel="noopener noreferrer"&gt;Hilt&lt;/a&gt; is used as a dependency injection library in the project so lets create the datastore module&lt;/p&gt;

&lt;p&gt;DatastoreModule.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Module
@InstallIn(ApplicationComponent::class)
class DataStoreModule {
    @Provides
    fun provideOnBoardPreference(@ApplicationContext context: Context): DataStore&amp;lt;Preferences&amp;gt;{
       return context.createDataStore(
            name = "com.wellnesscity.health"
        )
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have created a datastore module file that creates Preference Datastore dependency with the name &lt;code&gt;com.wellnesscity.health&lt;/code&gt;. Next we create a preference manager class&lt;/p&gt;

&lt;p&gt;DataStorePreferences.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class DataStorePreference @Inject constructor(private val preferences:DataStore&amp;lt;Preferences&amp;gt;) {

    suspend fun saveOnboarding(save:Boolean) {
        preferences.edit { onboardPreference-&amp;gt;
            onboardPreference[preferencesKey&amp;lt;Boolean&amp;gt;(ONBOARD_KEY)] = save
        }
    }

     fun fetchOnboarding() = preferences.data.map {onboardPreference-&amp;gt;
         onboardPreference[preferencesKey&amp;lt;Boolean&amp;gt;(ONBOARD_KEY)] ?: false  }

    suspend fun saveDiet(diet:String){
        preferences.edit {dietPreference-&amp;gt;
            dietPreference[preferencesKey&amp;lt;String&amp;gt;(DIET_KEY)] = diet
        }
    }

    fun fetchDiet() = preferences.data.map {dietPreference-&amp;gt;
        dietPreference[preferencesKey&amp;lt;String&amp;gt;(DIET_KEY)]?:"vegetarian"
    }

    companion object{
       const val ONBOARD_KEY = "onBoard"
        const val DIET_KEY = "diet"
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the DatastorePreference;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The methods &lt;code&gt;saveOnboarding()&lt;/code&gt;is used to ensure the onboarding page only displays once on the apps first install, you can check &lt;a href="https://github.com/kulloveth/WellnessCity/tree/onboardingSample" rel="noopener noreferrer"&gt;onboarding branch&lt;/a&gt; to test out the simplified part of that feature while &lt;code&gt;saveDiet()&lt;/code&gt; stores selected diet to the preference file so that when the user exits the page and come back they will still be seeing the recipe for the last diet they selected.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The save methods includes the suspend qualifier which is a coroutine key word that moves operation out of the main thread except specified otherwise.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;edit()&lt;/code&gt; function is used to write value into the preference file providing the type of value and a key to reference it when necessary.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;fetchOnboarding()&lt;/code&gt; and &lt;code&gt;fetchDiet()&lt;/code&gt; methods retrieved their respective preference values and acts according to the data present.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The fetch methods uses &lt;a href="https://codelabs.developers.google.com/codelabs/advanced-kotlin-coroutines/#0" rel="noopener noreferrer"&gt;Flow&lt;/a&gt; to retrieve the value ensuring that operations are run off the main thread.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next we are going to provide this class to the ViewModel classes that need them.&lt;/p&gt;

&lt;p&gt;OnboardingViewModel.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class OnboardingViewModel @ViewModelInject constructor(private val preference: DataStorePreference) :
    ViewModel() {
    fun saveOnboarding(save: Boolean) {
        viewModelScope.launch {
            preference.saveOnboarding(save)
        }
    }

    fun fetchOnboarding() = preference.fetchOnboarding().asLiveData()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Since the &lt;code&gt;saveOnboarding()&lt;/code&gt; from the DatastorePreference class is a suspend function it can only be called from a coroutine scope hence the need for &lt;code&gt;viewModelScope&lt;/code&gt; since we are in a viewModel class.&lt;/li&gt;
&lt;li&gt;With the help of the lifecycle extension we are able to convert the &lt;code&gt;fetchOnboarding()&lt;/code&gt; from flow to Livedata so we can observe from our Fragment class.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;same applies to the DietViewModel class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   fun saveDiet(diet:String){
        viewModelScope.launch {
            preference.saveDiet(diet)
        }
    }

    fun getDiet() = preference.fetchDiet().asLiveData()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now lets provide these ViewModel classes to Fragments that need them&lt;/p&gt;

&lt;p&gt;DieFragment.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; fun setupDiet(id: Int) {
        when (id) {
            R.id.vegan -&amp;gt; {
                viewModel.saveDiet("vegan")
            }
            R.id.whole -&amp;gt; {
                viewModel.saveDiet("Whole30")
            }
            R.id.gf -&amp;gt; {
                viewModel.saveDiet("Gluton Free")
            }
            R.id.vegy -&amp;gt; {
                viewModel.saveDiet("Vegetarian")
            }
            R.id.ketogen -&amp;gt; {
                viewModel.saveDiet("Ketogenic")
            }
            R.id.lactovegy -&amp;gt; {
                viewModel.saveDiet("Lacto-vegetarian")
            }
            R.id.ovovegy -&amp;gt; {
                viewModel.saveDiet("Ovo-vegetarian")
            }
            R.id.pesce -&amp;gt; {
                viewModel.saveDiet("Pescetarian")
            }
            R.id.palio -&amp;gt; {
                viewModel.saveDiet("Paleo")
            }
            R.id.primal -&amp;gt; {
                viewModel.saveDiet("Primal")
            }
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; viewModel.getDiet().observe(requireActivity(), Observer {
                diet = it
            })
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are calling the &lt;code&gt;saveDiet()&lt;/code&gt; from DietVieModel class to save each selected diet and then we retrieve the diet by observing the &lt;code&gt;getDiet()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;Same applies to the Onboarding,&lt;/p&gt;

&lt;p&gt;SplashFragment.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  viewModel.fetchOnboarding().observe(requireActivity(), Observer {
            if (it == true) {
                view?.findNavController()
                   ?.navigate(SplashFragmentDirections.actionSplashFragmentToWelcomeFragment())
            } else {
                requireView().findNavController()
                    .navigate(SplashFragmentDirections.actionSplashFragmentToOnboardingFragment())
            }
        })
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OnboardingFragment.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  binding?.buttonNext?.setOnClickListener {
                                viewModel.saveOnboarding(true)
                            requireView().findNavController()
                                .navigate(OnboardingFragmentDirections.actionOnboardingFragmentToWelcomeFragment())
                        }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We Observe the &lt;code&gt;fetchOnboarding()&lt;/code&gt; from the OnboardingViewModel class, depending on the value returned we either navigate to the onboarding page or navigate to the Welcome page while in the OnboardingFragment the value is saved to true once the finish button present on the final page for onboarding is clicked. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F92mkhxr65nqu0gbe89uh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F92mkhxr65nqu0gbe89uh.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
You can reference the full project &lt;a href="https://github.com/kulloveth/WellnessCity" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope you find this helpful while implementing preference datastore. Kindly drop your question and suggestion in the comment section &lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>datastore</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Android Animation in Kotlin: Onboarding screen with Lottie Animation, Navigation Component and ViewPager2</title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Sat, 07 Nov 2020 20:17:44 +0000</pubDate>
      <link>https://dev.to/kulloveth/android-animation-onboarding-screen-with-lottie-animation-navigation-component-and-viewpager2-1dhb</link>
      <guid>https://dev.to/kulloveth/android-animation-onboarding-screen-with-lottie-animation-navigation-component-and-viewpager2-1dhb</guid>
      <description>&lt;p&gt;Animation in android is a way to make your app lively while passing information to the user. There are different APIs available in android you can use to create animations read more about it &lt;a href="https://developer.android.com/training/animation/overview" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For this post we will be using a library called &lt;a href="https://airbnb.io/lottie/#/" rel="noopener noreferrer"&gt;Lottie&lt;/a&gt; which makes it easier to add animations to your application. What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating an onboarding screen &lt;/li&gt;
&lt;li&gt;Using Navigation Component&lt;/li&gt;
&lt;li&gt;Animations with Lottie&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Forinbfmutryuouad2f5e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Forinbfmutryuouad2f5e.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Onboarding screen is mainly used to explain the features of an app using images, text or animations. To begin add the following dependencies to your app module build.gradle file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//navigation component
Implementation "androidx.navigation:navigation-fragment-ktx:2.3.1"
implementation "androidx.navigation:navigation-ui-ktx:2.3.1"
//viewpager2
implementation "androidx.viewpager2:viewpager2:1.0.0"
//indicator
implementation 'me.relex:circleindicator:2.1.4'
//lottie
implementation "com.airbnb.android:lottie:3.4.1"
//datastore
implementation "androidx.datastore:datastore-preferences:1.0.0-alpha02"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to enable safe args to ensure type-safety by adding the plugin to gradle. Add the following to the top level build.gradle file in the dependencies block&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.1"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then apply the plugin in the app module build.gradle file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apply plugin: 'androidx.navigation.safeargs.kotlin'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will be navigating into the onboarding screen from the splash screen. &lt;br&gt;
The flow of navigation is Splash screen -&amp;gt; Onboarding Screen -&amp;gt; Main Page. Following the single activity principle the project will have only one activity(Main Activity) and three(3) fragments.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new project with an empty template&lt;/li&gt;
&lt;li&gt;Right click on res folder and select New -&amp;gt; Android Resource File&lt;/li&gt;
&lt;li&gt;Enter a name for your navigation graph file, select Navigation as Resource type and click ok&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;nav_graph.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;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/nav_graph"
    app:startDestination="@id/splashFragment"&amp;gt;

    &amp;lt;fragment
        android:id="@+id/splashFragment"
        android:name="com.wellnesscity.health.ui.intro.SplashFragment"
        android:label="fragment_splash"
        tools:layout="@layout/fragment_splash" &amp;gt;
        &amp;lt;action
            android:id="@+id/action_splashFragment_to_welcomeFragment"
            app:destination="@id/welcomeFragment"
            app:popUpTo="@id/splashFragment"
            app:popUpToInclusive="true" /&amp;gt;
        &amp;lt;action
            android:id="@+id/action_splashFragment_to_onboardingFragment"
            app:destination="@id/onboardingFragment"
            app:popUpTo="@id/splashFragment"
            app:popUpToInclusive="true" /&amp;gt;
    &amp;lt;/fragment&amp;gt;
    &amp;lt;fragment
        android:id="@+id/onboardingFragment"
        android:name="com.wellnesscity.health.ui.intro.OnboardingFragment"
        android:label="fragment_onboarding"
        tools:layout="@layout/fragment_onboarding" &amp;gt;
        &amp;lt;action
            android:id="@+id/action_onboardingFragment_to_welcomeFragment"
            app:destination="@id/welcomeFragment"
            app:popUpTo="@id/onboardingFragment"
            app:popUpToInclusive="true" /&amp;gt;
    &amp;lt;/fragment&amp;gt;
    &amp;lt;fragment
        android:id="@+id/welcomeFragment"
        android:name="com.wellnesscity.health.ui.welcome.WelcomeFragment"
        android:label="fragment_welcome"
        tools:layout="@layout/fragment_welcome" &amp;gt;
    &amp;lt;/fragment&amp;gt;

&amp;lt;/navigation&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The nav_graph contains splash fragment which is the start destination, onboarding fragment and finally the welcome fragment. The first fragment added is set as the start destination which you can change later if need be by changing the id in the &lt;code&gt;app:startDestination&lt;/code&gt; which is an attribute in the &lt;code&gt;&amp;lt;navigation&amp;gt;&lt;/code&gt; tag to a different fragment id that you choose. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To add the fragments; in the design view at the top left corner click on the add button to see a list of fragments to choose from. &lt;/li&gt;
&lt;li&gt;To add an action that connects the SplashFragment to the OnboardingFragment; hover on the SplashFragment and drag the circular connection point that shows up to the OnboardingFragment&lt;/li&gt;
&lt;li&gt;Repeat same for OnboardingFragment to WelcomeFragment&lt;/li&gt;
&lt;li&gt;The action tag represents possible path to a destination&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:destination&lt;/code&gt; contains the id to the destination fragment&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:popUpTo&lt;/code&gt; contains the id to the fragment you want directly in your back stack . &lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:popUpToInclusive="true"&lt;/code&gt; indicates that the popUpTo fragment should also be removed from the back stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Connecting the nav_graph to the MainActivity&lt;/p&gt;

&lt;p&gt;activity_main.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.activity.MainActivity"&amp;gt;

    &amp;lt;fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true" /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;android:name&lt;/code&gt; attribute indicates the type of navigation been used&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:navGraph&lt;/code&gt; attribute connects the layout to a navigation graph&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:defaultNavHost="true"&lt;/code&gt; intercepts the system back button&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The nav-graph is all setup, attached to the MainActivity and ready for use&lt;/p&gt;

&lt;p&gt;fragment_splash.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;FrameLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.intro.SplashFragment"&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;ImageView
        android:id="@+id/splash_iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:srcCompat="@drawable/splash_screen" /&amp;gt;

&amp;lt;/FrameLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  N:B
&lt;/h2&gt;

&lt;p&gt;Dagger Hilt is used as a dependency injection library in the full project and you will be seeing some of its annotations.&lt;/p&gt;

&lt;p&gt;SplashFragment.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@AndroidEntryPoint
class SplashFragment : Fragment() {
    private var binding: FragmentSplashBinding? = null
    @Inject lateinit var prefs:DataStore&amp;lt;Preferences&amp;gt;
    private val handler = Handler()
    private val runnable = Runnable {

//saves the onboarding screen to datastore the first its viewed and ensures it only shows up ones after installation
        lifecycleScope.launch {
        prefs.data.collectLatest {
            if (it[preferencesKey&amp;lt;Boolean&amp;gt;("onBoard")] == true)
                requireView().findNavController()
                    .navigate(SplashFragmentDirections.actionSplashFragmentToWelcomeFragment())
            else
                requireView().findNavController()
                    .navigate(SplashFragmentDirections.actionSplashFragmentToOnboardingFragment())
        }
    }}

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentSplashBinding.inflate(layoutInflater)
        return binding?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        requireActivity().window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN)
    }

    override fun onResume() {
        super.onResume()
        handler.postDelayed(runnable,3000)
    }

    override fun onPause() {
        super.onPause()
        handler.removeCallbacks(runnable)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the SplashFragment we check if the onboarding has been displayed before, if yes then we navigate to the welcome screen if not we navigate into the  OnboardingFragment because we want the onboarding to be displayed only when the app is first installed. To achieve this we used the preference datastore to save a boolean value to true on first installation.&lt;/p&gt;

&lt;p&gt;The OnboardingFragment contains three item describing the content of the app, these items will be displayed with ViewPager2 and a RecyclerAdapter. You can either create your own animations with &lt;a href="https://airbnb.io/lottie/#/after-effects" rel="noopener noreferrer"&gt;after effect&lt;/a&gt; and export as a json file or choose from &lt;a href="https://lottiefiles.com/popular" rel="noopener noreferrer"&gt;lottie website&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To add the json files to the asset folder;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Right click on your app folder&lt;/li&gt;
&lt;li&gt;select New-&amp;gt;folder-&amp;gt;AssetsFolder&lt;/li&gt;
&lt;li&gt;copy and paste them in the asset folder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;slide_item_layout.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="15dp"&amp;gt;

    &amp;lt;com.airbnb.lottie.LottieAnimationView
        android:id="@+id/imageSlideIcon"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        app:layout_constraintTop_toTopOf="parent"
        app:lottie_autoPlay="true"
        app:lottie_fileName="diet.json"
        app:lottie_loop="true" /&amp;gt;

    &amp;lt;TextView
        android:id="@+id/textTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="Title Text"
        android:textColor="@color/colorPrimary"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintTop_toBottomOf="@id/imageSlideIcon" /&amp;gt;

    &amp;lt;TextView
        android:id="@+id/textDescription"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:gravity="center"
        android:text="Description Text"
        android:textColor="@color/colorTextSecondary"
        android:textSize="15sp"
        app:layout_constraintTop_toBottomOf="@id/textTitle" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lottie files can be referenced from two folders: raw and asset folder and for this post the json files are in the later. Notice the Lottie view widget in the item layout above;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;app:lottie_autoPlay="true"&lt;/code&gt; starts the animation as soon the screen is in view&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:lottie_fileName="diet.json"&lt;/code&gt; attribute is used to add file name when the json file is in asset folder&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;app:lottie_loop="true"&lt;/code&gt; attribute keeps the animation active until user exits the screen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;IntroSlide&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data class IntroSlide (
    val title: String,
    val description: String,
    val icon: String
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above model class is used to represent each item present in the onboarding page &lt;/p&gt;

&lt;p&gt;IntroSliderAdapter.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class IntroSliderAdapter(private val introSlides: List&amp;lt;IntroSlide&amp;gt;)
    : RecyclerView.Adapter&amp;lt;IntroSliderAdapter.IntroSlideViewHolder&amp;gt;(){
//for adding text to speech listener in the onboarding fragment
    var onTextPassed: ((textView:TextView) -&amp;gt; Unit)? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntroSlideViewHolder {
        return IntroSlideViewHolder(
            SlideItemContainerBinding.inflate(LayoutInflater.from(parent.context),parent,false)
        )
    }

    override fun getItemCount(): Int {
        return introSlides.size
    }

    override fun onBindViewHolder(holder: IntroSlideViewHolder, position: Int) {
        holder.bind(introSlides[position])
    }

    inner class IntroSlideViewHolder(private val binding: SlideItemContainerBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bind(introSlide: IntroSlide) {
            binding.textTitle.text = introSlide.title
            binding.textDescription.text = introSlide.description
            binding.imageSlideIcon.imageAssetsFolder = "images";
            binding.imageSlideIcon.setAnimation(introSlide.icon)
            onTextPassed?.invoke(binding.textTitle)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above adapter is a normal recyclerView adapter containing a viewHolder with an item layout. In the viewHolder we direct Lottie to where the images are stored by calling &lt;code&gt;.imageAssetsFolder&lt;/code&gt; on the Lottie view and setting it to &lt;code&gt;"images"&lt;/code&gt; and then call &lt;code&gt;setAnimation&lt;/code&gt; on the view as well.&lt;/p&gt;

&lt;p&gt;fragment_onboarding.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.intro.OnboardingFragment"&amp;gt;

    &amp;lt;!-- TODO: Update blank fragment layout --&amp;gt;
    &amp;lt;androidx.viewpager2.widget.ViewPager2
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@id/indicator"/&amp;gt;

    &amp;lt;me.relex.circleindicator.CircleIndicator3
        android:id="@+id/indicator"
        android:layout_width="wrap_content"
        android:layout_height="@dimen/indicator_height"
        app:ci_drawable="@drawable/indicator_active"
        app:ci_drawable_unselected="@drawable/indicator_inactive"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toTopOf="@id/buttonNext" /&amp;gt;

    &amp;lt;com.google.android.material.button.MaterialButton
        android:id="@+id/buttonNext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="@dimen/standard_padding"
        android:text="@string/next"
        android:textColor="@android:color/white"
        android:textStyle="bold"
        app:backgroundTint="@color/colorPrimaryDark"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The onboarding fragment layout contains the &lt;a href="https://github.com/ongakuer/CircleIndicator" rel="noopener noreferrer"&gt;relex indicator&lt;/a&gt; which is a library that automatically controls indicators with  view pagers. &lt;/p&gt;

&lt;p&gt;OnboardingFragment.kt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@AndroidEntryPoint
class OnboardingFragment : Fragment() {
    private var binding: FragmentOnboardingBinding? = null
    @Inject
    lateinit var prefs: DataStore&amp;lt;Preferences&amp;gt;

//the items are added to the adapter 
    private val introSliderAdapter = IntroSliderAdapter(
        listOf(
            IntroSlide(
                "Health Tips / Advice",
                "Discover tips and advice to help you to help maintain transform and main your health",
                "exercise.json"
            ),
            IntroSlide(
                "Diet Tips / Advice",
                "Find out basics of health diet and good nutrition, Start eating well and keep a balanced diet",
                "diet.json"
            ),
            IntroSlide(
                "Covid 19 Symptoms/Prevention tips",
                "Get regular Reminders of Covid-19 prevention tips ensuring you stay safe",
                "covid19.json"
            )
        )
    )

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = FragmentOnboardingBinding.inflate(layoutInflater)
        return binding?.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
//set the adapter to the viewpager2
        binding?.viewPager?.adapter = introSliderAdapter
//sets the viewpager2 to the indicator
        binding?.indicator?.setViewPager(binding?.viewPager)

binding?.viewPager?.registerOnPageChangeCallback(
            object : ViewPager2.OnPageChangeCallback() {

                override fun onPageSelected(position: Int) {
                    super.onPageSelected(position)

/*
*check if its the last page, change text on the button
*from next to finish and set the click listener to
*to navigate to welcome screen else set the text to next
* and click listener to move to next page
*/
   if (position == introSliderAdapter.itemCount - 1) {
//this animation is added to the finish button
                        val animation = AnimationUtils.loadAnimation(
                            requireActivity(),
                            R.anim.app_name_animation
                        )
                        binding?.buttonNext?.animation = animation
                        binding?.buttonNext?.text = "Finish"
                        binding?.buttonNext?.setOnClickListener {
                            lifecycleScope.launch {
                                saveOnboarding()
                            }
                            requireView().findNavController()
                                .navigate(OnboardingFragmentDirections.actionOnboardingFragmentToWelcomeFragment())
                        }
                    } else {
                        binding?.buttonNext?.text = "Next"
                        binding?.buttonNext?.setOnClickListener {
                            binding?.viewPager?.currentItem?.let {
                                binding?.viewPager?.setCurrentItem(it + 1, false)
                            }
                        }
                    }
                }
            })
    }

//suspend function to save the onboarding to datastore
    suspend fun saveOnboarding() {
        prefs.edit {
            val oneTime = true
            it[preferencesKey&amp;lt;Boolean&amp;gt;("onBoard")] = oneTime
        }
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the onboardingFragment the IntroSLideAdapter is setup with the list of Items for the Onboarding page, connects the adapter to ViewPager2 and setup the indicator with viewPager2. In the &lt;code&gt;onPageChangeCallback&lt;/code&gt; we check if the currentPage is the last one and change the text on the button to finish and navigate into the WelcomeFragment if not the text remains Next and when clicked moves to the next page, finally we have &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Forinbfmutryuouad2f5e.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Forinbfmutryuouad2f5e.gif" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This sample code is part of a community project &lt;a href="https://github.com/kulloveth/WellnessCity" rel="noopener noreferrer"&gt;WellnessCity&lt;/a&gt;, you can check out just the onboarding branch &lt;a href="https://github.com/kulloveth/WellnessCity/tree/onboardingSample" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have any suggestions or question kindly drop them in the comment section. Thank you&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>lottie</category>
      <category>navigation</category>
    </item>
    <item>
      <title>ExpandableRecyclerView: Expandable Items With Room Database in Kotlin</title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Fri, 01 May 2020 17:39:38 +0000</pubDate>
      <link>https://dev.to/kulloveth/expandablerecyclerview-expandable-items-with-room-database-in-kotlin-part-1-5190</link>
      <guid>https://dev.to/kulloveth/expandablerecyclerview-expandable-items-with-room-database-in-kotlin-part-1-5190</guid>
      <description>&lt;p&gt;Sometimes we want to display an extra content about the list items  which does not necessarily need a separate screen usually called the detail screen,this is where the Expandable RecyclerView comes in, We are going to be learning how to create an expandable recyclerview using the &lt;a href="https://github.com/thoughtbot/expandable-recycler-view"&gt;&lt;code&gt;THOUGHBOT&lt;/code&gt;&lt;/a&gt; expandable recyclerview library. We will also be fetching our items from local database using  &lt;a href="https://developer.android.com/topic/libraries/architecture/room"&gt;&lt;code&gt;Room Persistence Library&lt;/code&gt;&lt;/a&gt;  which is part of the &lt;a href="https://developer.android.com/topic/libraries/architecture"&gt;&lt;code&gt;Android Architecture Components&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will be displaying the list of continents and some of the countries under them from local database which is added only once when the database is created, the end result should look like the below images.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V0s3eWFb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lh4x1ebg3bbqps9iame8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V0s3eWFb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/lh4x1ebg3bbqps9iame8.png" alt="ParentView"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--F1zTSMh---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yt788kzthmxnma83eudj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--F1zTSMh---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/yt788kzthmxnma83eudj.png" alt="Parent with child"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you create a new project with empty activity add the following dependencies to your app level build.gradle&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Room components
    implementation "androidx.room:room-runtime:$rootProject.roomVersion"
    kapt "androidx.room:room-compiler:$rootProject.roomVersion"
    implementation "androidx.room:room-ktx:$rootProject.roomVersion"
    androidTestImplementation "androidx.room:room-testing:$rootProject.roomVersion"

//Gson
    implementation 'com.google.code.gson:gson:2.8.6'

// Lifecycle components
    implementation "androidx.lifecycle:lifecycle-extensions:$rootProject.archLifecycleVersion"
    kapt "androidx.lifecycle:lifecycle-compiler:$rootProject.archLifecycleVersion"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$rootProject.archLifecycleVersion"

// Kotlin components
 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
 api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$rootProject.coroutines"
 api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$rootProject.coroutines"

// Material design
    implementation "com.google.android.material:material:$rootProject.materialVersion"

//Expandable
    implementation 'com.thoughtbot:expandablerecyclerview:1.3'
    implementation 'com.thoughtbot:expandablecheckrecyclerview:1.4'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also add the versions to the project level build.gradle&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ext {
    roomVersion = '2.2.5'
    archLifecycleVersion = '2.2.0'
    coreTestingVersion = '2.1.0'
    materialVersion = '1.1.0'
    coroutines = '1.3.4'
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;make sure the following plugins are present at the top of the app level build. gradle&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There are three major classes required while working with Room,the &lt;a href="https://developer.android.com/training/data-storage/room/defining-data"&gt;&lt;code&gt;Entity&lt;/code&gt;&lt;/a&gt; class which represents a table in the database,the &lt;a href="https://developer.android.com/training/data-storage/room/accessing-data"&gt;&lt;code&gt;DAO&lt;/code&gt;&lt;/a&gt; class just as its name implies a data access object which contains the methods used for accessing the database, the &lt;a href="https://developer.android.com/reference/androidx/room/Database"&gt;&lt;code&gt;database&lt;/code&gt;&lt;/a&gt; class.&lt;/p&gt;

&lt;p&gt;ContinentEntity.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.model

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters

@Entity(tableName = "continent-table")
@TypeConverters(ContinentConverter::class)
data class ContinentEntity
 (@PrimaryKey @ColumnInfo(name = "continent") 
val continentName: String,  val countrys: List&amp;lt;Country&amp;gt;
)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This class has the @Entity annotation passing the table name to be created into its parameter which is optional if you don't want the class name to be used as the table name, @ColumnInfo tells the database to use continent as the column name instead of continentName variable and @PrimaryKey which all tables must have. Also notice the @TypeConverters which is the annotation that tells room to convert the List with the ContinentConverter class&lt;/p&gt;

&lt;p&gt;ContinentConverter.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.model

import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type
import java.util.*


class ContinentConverter {
companion object {
        var gson: Gson = Gson()

  @TypeConverter
  @JvmStatic
  fun stringToSomeObjectList(data: String?): List&amp;lt;Country&amp;gt; {
  val listType: Type =
  object : TypeToken&amp;lt;List&amp;lt;Country?&amp;gt;?&amp;gt;() {}.getType()
  return gson.fromJson(data, listType)
        }

   @TypeConverter
   @JvmStatic
   fun someObjectListToString(someObjects: List&amp;lt;Country&amp;gt;?): String {
   return gson.toJson(someObjects)
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is the converter class that has @TypeConverter on each method performing the conversion with Gson library&lt;/p&gt;

&lt;p&gt;ContinentDao.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.db

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.ContinentEntity
import 
com.developer.kulloveth.expandablelistsamplewithroom.data.model.Continents


@Dao
interface ContinentDao {
    @Query("SELECT * from `continent-table` ORDER BY continent ASC")
    fun getAllContinent(): LiveData&amp;lt;List&amp;lt;ContinentEntity&amp;gt;&amp;gt;

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(continent: ContinentEntity)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is the dao interface for accessing the database, getAllContinent method has the @Query annotation that fetches all data in ascending order, it returns a &lt;a href="https://developer.android.com/topic/libraries/architecture/livedata.html"&gt;&lt;code&gt;LiveData&lt;/code&gt;&lt;/a&gt; which helps keep the data updated and automatically runs the operation asynchronously on background thread. The insert method has the @Insert annotation that inserts data taking care of conflicts that might occur, it uses the suspend function to indicate that the method requires time to execute since we don't want to block the main thread. &lt;/p&gt;

&lt;p&gt;ContinentDatabase.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.db

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.developer.kulloveth.expandablelistsamplewithroom.data.DataGenerator
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.ContinentEntity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Database(entities = [ContinentEntity::class], version = 1, exportSchema = false)

abstract class ContinentDatabase : RoomDatabase() {

    abstract fun continentDao(): ContinentDao


    companion object {
        @Volatile
        private var INSTANCE: ContinentDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): ContinentDatabase {

            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: buildDatabase(context, scope).also {
                    INSTANCE = it
                }
            }
        }

 private fun buildDatabase(context: Context, scope: CoroutineScope): ContinentDatabase {
   return Room.databaseBuilder(context, ContinentDatabase::class.java, "place_db")
 .addCallback(object : RoomDatabase.Callback() 
 override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)

               scope.launch {
                 INSTANCE?.let {
                      for (continent: ContinentEntity in DataGenerator.getContinents()) {
            it.continentDao().insert(
                  ContinentEntity(
                           continent.continentName,
                                            continent.countrys
                                        ) )
                                }}}}}).build()
}}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is the database class which must be an abstract class and must contain an abstract method representing the dao interface class,it has @Database with the its entity, version and set export-schema to false since we are not exporting the database into a folder. The getDatabase method is a singleton which ensures that only one instance of the database is open at any time, we also added a roomCallback to insert the data only once when the room is created using its onCreate method. notice that the insert method is called within a coroutine scope since its a suspend function to ensure the operation is performed on the background thread.&lt;/p&gt;

&lt;p&gt;DataGenerator.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data

import com.developer.kulloveth.expandablelistsamplewithroom.data.model.ContinentEntity
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Country

class DataGenerator {

    companion object {
        fun getContinents(): List&amp;lt;ContinentEntity&amp;gt; {
            return listOf(
    ContinentEntity("Europe", europeCountrys()),
    ContinentEntity("Africa", africaCountrys()),
    ContinentEntity("Asia", asiaCountrys()),
    ContinentEntity("North America", northAmericaCountrys()),
    ContinentEntity("South America", southAmericaCountrys()),
    ContinentEntity("Antarctica", antarcticaCountrys()),
    ContinentEntity("Oceania", oceaniaCountrys())
            )
        }

        fun europeCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("Germany"),
                Country("Italy"),
                Country("France"),
                Country("United Kingdom"),
                Country("NertherLand")
            )
        }

        fun africaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("South Africa"),
                Country("Nigeria"),
                Country("Kenya"),
                Country("Ghana"),
                Country("Ethiopia")
            )

        }

        fun asiaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("Japan"),
                Country("India"),
                Country("Indonesi"),
                Country("China"),
                Country("Thailand")
            )
        }

        fun northAmericaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("United States"),
                Country("Mexico"),
                Country("Cuba"),
                Country("Green Land")
            )
        }


        fun southAmericaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("Brazil"),
                Country("Argentina"),
                Country("Columbia"),
                Country("Peru"),
                Country("Chile")
            )}

       fun antarcticaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("Esperenza Base"),
                Country("Villa az Estrellaz"),
                Country("General Bernando O'Higging"),
                Country("Bellgrano II base"),
                Country("Carlini Base") )}

        fun oceaniaCountrys(): List&amp;lt;Country&amp;gt; {
            return listOf(
                Country("Australia"),
                Country("Newzeland"),
                Country("Fiji"),
                Country("Samao"),
                Country("Federated States")
            )}}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Next we are going to be creating the Adapter, its data class,observe the data we added to Room and setup the recyclerView.&lt;/p&gt;

&lt;p&gt;Continent.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.model
import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup
data class Continent(
    val continentName: String, val countries: List&amp;lt;Country&amp;gt;
):  ExpandableGroup&amp;lt;Country&amp;gt;(continentName, countries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Country.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.model

import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize

@Parcelize
data class Country(val countryName: String) : Parcelable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Continent class is the Parent class to be used with the adapter which will be extending the ExpandableGroup passing the child class Country&lt;/p&gt;

&lt;p&gt;continent_layout.xml&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="0dp"
    app:cardUseCompatPadding="true"&amp;gt;

    &amp;lt;androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"&amp;gt;


        &amp;lt;ImageView
            android:id="@+id/arrow"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:layout_marginEnd="16dp"
            android:src="@drawable/ic_arrow_drop_down_black_24dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" /&amp;gt;

        &amp;lt;TextView
            android:id="@+id/continent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="?listPreferredItemPaddingLeft"
            android:textAppearance="@style/TextAppearance.AppCompat.Large"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" /&amp;gt;
    &amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;

&amp;lt;/androidx.cardview.widget.CardView&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;countrys_layout.xml&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:padding="0dp"&amp;gt;

    &amp;lt;TextView
        android:id="@+id/countryName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        tools:text="Niger" /&amp;gt;

    &amp;lt;View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="@android:color/black" /&amp;gt;

&amp;lt;/androidx.cardview.widget.CardView&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The above layouts are the parent and child layout for the items to be referenced in their respective viewholder&lt;/p&gt;

&lt;p&gt;MainViewHolder.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data

import android.view.View
import android.widget.ImageView
import android.widget.TextView
import com.developer.kulloveth.expandablelistsamplewithroom.R
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Continent
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Country
import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder


class CountryViewHolder(itemView: View) : ChildViewHolder(itemView) {
    val countryName = itemView.findViewById&amp;lt;TextView&amp;gt;(R.id.countryName)

    fun bind(country: Country) {
        countryName.text = country.countryName
    }
}

class ContinentViewHolder(itemView: View) : GroupViewHolder(itemView) {
    val continentName = itemView.findViewById&amp;lt;TextView&amp;gt;(R.id.continent)
    val arrow = itemView.findViewById&amp;lt;ImageView&amp;gt;(R.id.arrow)

    fun bind(continent: Continent) {
        continentName.text = continent.continentName
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The MainViewHolder is a kotlin file containing both the parent viewholder and child viewholder&lt;/p&gt;

&lt;p&gt;ContinentAdapter.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.ui

import android.view.LayoutInflater
import android.view.ViewGroup
import com.developer.kulloveth.expandablelistsamplewithroom.R
import com.developer.kulloveth.expandablelistsamplewithroom.data.ContinentViewHolder
import com.developer.kulloveth.expandablelistsamplewithroom.data.CountryViewHolder
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Continent
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Country
import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter
import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup

class ContinentAdapter(groups: List&amp;lt;ExpandableGroup&amp;lt;*&amp;gt;&amp;gt;?) :
    ExpandableRecyclerViewAdapter&amp;lt;ContinentViewHolder, CountryViewHolder&amp;gt;(
        groups
    ) {


    override fun onCreateGroupViewHolder(parent: ViewGroup?, viewType: Int): ContinentViewHolder {
        val itemView =
            LayoutInflater.from(parent?.context).inflate(R.layout.continent_layout, parent, false)
        return ContinentViewHolder(itemView)
    }

    override fun onCreateChildViewHolder(parent: ViewGroup?, viewType: Int): CountryViewHolder {
        val itemView =
            LayoutInflater.from(parent?.context).inflate(R.layout.countrys_layout, parent, false)
        return CountryViewHolder(itemView)
    }

    override fun onBindChildViewHolder(
        holder: CountryViewHolder?,
        flatPosition: Int,
        group: ExpandableGroup&amp;lt;*&amp;gt;?,
        childIndex: Int
    ) {
        val country: Country = group?.items?.get(childIndex) as Country
        holder?.bind(country)
    }

    override fun onBindGroupViewHolder(
        holder: ContinentViewHolder?,
        flatPosition: Int,
        group: ExpandableGroup&amp;lt;*&amp;gt;?
    ) {
        val continent: Continent = group as Continent
        holder?.bind(continent)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The adapter class accepts a List of ExpandableGroup type extending ExpandableAdapter with the parent and child viewholder respectively,Both parent and child has its own oncreateViewHolder and onBindViewHolder&lt;/p&gt;

&lt;p&gt;Repository.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.data.model

import androidx.lifecycle.LiveData
import com.developer.kulloveth.expandablelistsamplewithroom.data.db.ContinentDao

class Repository(continentDao: ContinentDao) {

    val allContinents: LiveData&amp;lt;List&amp;lt;ContinentEntity&amp;gt;&amp;gt; = continentDao.getAllContinent()


}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;MainActivityViewModel.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.ui

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import com.developer.kulloveth.expandablelistsamplewithroom.data.db.ContinentDatabase
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.ContinentEntity
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Repository

class MainActivityViewModel(application: Application) : AndroidViewModel(application) {

    private val repository: Repository
    val continents: LiveData&amp;lt;List&amp;lt;ContinentEntity&amp;gt;&amp;gt;
    init {
        val continentDao = ContinentDatabase.getDatabase(application, viewModelScope).continentDao()
        repository = Repository(continentDao)
        continents = repository.allContinents
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The repository pattern helps to separate business logic from the UI logic which is most useful when you are fetching data from different sources. The viewmodel class provides data to the UI and also survives configuration changes&lt;/p&gt;

&lt;p&gt;activity_main.xml&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".data.ui.MainActivity"&amp;gt;

    &amp;lt;androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:id="@+id/rvConinent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" /&amp;gt;

&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;MainActivity.kt&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.developer.kulloveth.expandablelistsamplewithroom.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.developer.kulloveth.expandablelistsamplewithroom.R
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.ContinentEntity
import com.developer.kulloveth.expandablelistsamplewithroom.data.model.Continent
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    lateinit var viewModel: MainActivityViewModel
    val continents = ArrayList&amp;lt;Continent&amp;gt;()
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
   viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java]

    viewModel.continents.observe(this, Observer {

   for (continentEntity: ContinentEntity in it) {

    val continent = Continent(continentEntity.continentName, continentEntity.countrys)
                continents.add(continent)}
            val adapter = ContinentAdapter(continents)
            rvConinent.apply {
                layoutManager = LinearLayoutManager(this@MainActivity)
                rvConinent.adapter = adapter
            } })}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;finally the main layout and its activity that Observes the data from the MainActivityViewModel, adds to a new list and displays on the recyclerView.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T8Sifrr---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bm7rnj0ne29bzbtpqhpa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T8Sifrr---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bm7rnj0ne29bzbtpqhpa.gif" alt="Final Result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;kindly leave your Feedback and suggestions  in the comment below &lt;a href="https://www.twitter.com/kulloveth"&gt;&lt;code&gt;TWITTER&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kulloveth"&gt;
        kulloveth
      &lt;/a&gt; / &lt;a href="https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom"&gt;
        ExpandableRecyclerViewSampleWithRoom
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A project showing the continents of the world with some of there countries to demonstrate Expadable RecyclerView with ROOM
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
ExpandableRecyclerViewSampleWithRoom&lt;/h1&gt;
&lt;p&gt;A project showing the continents of the world with some of there countries to demonstrate Expandable RecyclerView with ROOM&lt;/p&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom/blob/master/app/screenshots/second.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PTOHtZdc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom/raw/master/app/screenshots/second.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;/td&gt;
 &lt;td&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom/blob/master/app/screenshots/first.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CdV0hBCN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom/raw/master/app/screenshots/first.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kulloveth/ExpandableRecyclerViewSampleWithRoom"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>room</category>
      <category>android</category>
      <category>kotlin</category>
      <category>recyclerview</category>
    </item>
    <item>
      <title>Navigation Component: How to pass data between fragments using Bundle and ViewModel </title>
      <dc:creator>Loveth Nwokike</dc:creator>
      <pubDate>Thu, 26 Mar 2020 10:56:32 +0000</pubDate>
      <link>https://dev.to/kulloveth/navigation-component-how-to-pass-data-between-fragments-using-bundle-and-viewmodel-28nc</link>
      <guid>https://dev.to/kulloveth/navigation-component-how-to-pass-data-between-fragments-using-bundle-and-viewmodel-28nc</guid>
      <description>&lt;p&gt;Navigation Component is one of the Android jetpacks library. Its purpose is to have only one activity that serves as an entry point to your application using fragment for all UI screen. It comes with a lot of benefits which includes handling fragment transaction,control back stack by default, viewmodel support which I'm going to be explaining in this post&lt;/p&gt;

&lt;p&gt;This post demonstrates how to pass data between two fragments using both viewmodel and Bundle. &lt;/p&gt;

&lt;p&gt;Bundle is used to pass data between both activities and fragments, it maps values to String keys and then uses the key to retrieve the value.&lt;/p&gt;

&lt;p&gt;Viewmodel is a helper class designed to manage UI related data in a life-cycle conscious way.It is responsible for preparing data for the UI and therefore helps to separate the view from business logics. Fragments can share a viewmodel using their activity scope to handle communication between each other.&lt;/p&gt;

&lt;p&gt;The project sample fetches data from &lt;a href="https://restcountries.eu/rest/v2/all" rel="noopener noreferrer"&gt;https://restcountries.eu/rest/v2/all&lt;/a&gt; which displays all countries on the screen, when the user clicks on each country it opens up the detail fragment showing some information about the country.This project is in kotlin and the final ui is shown below&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxxm3v5893y4mfyq19kze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxxm3v5893y4mfyq19kze.png" alt="Countries"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzfoj8ar1b4wujn2g4xvv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzfoj8ar1b4wujn2g4xvv.png" alt="Translation"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk1beaxo2vnrrdfbh7fzs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk1beaxo2vnrrdfbh7fzs.png" alt="Languages"&gt;&lt;/a&gt;&lt;br&gt;
To use Navigation component we need to add its dependency to build.gradle, I will also be listing other dependencies used in the project. First apply kotlin kapt at the top of the gradle file &lt;code&gt;apply plugin: 'kotlin-kapt'&lt;/code&gt; then add the following dependencies&lt;/p&gt;

&lt;p&gt;Dependencies&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
 //svgLoader
    implementation 'com.github.ar-android:AndroidSvgLoader:1.0.2'
   //navigation component
    implementation 'androidx.navigation:navigation-fragment-ktx:2.2.1'
    implementation 'androidx.navigation:navigation-ui-ktx:2.2.1'
   //viewpager2
    implementation "androidx.viewpager2:viewpager2:1.0.0"
    implementation 'com.google.android.material:material:1.1.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.cardview:cardview:1.0.0'
    //retrofit
    implementation 'com.squareup.retrofit2:converter-gson:2.7.1'
    implementation 'com.google.code.gson:gson:2.8.6'
    implementation 'com.squareup.retrofit2:retrofit:2.7.1'
    //rxjava2
    implementation 'io.reactivex.rxjava2:rxjava:2.2.13'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    def lifecycle_version = "2.2.0"
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;RetrofitService.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.api
import io.reactivex.Single
import kulloveth.developer.com.countrydetails.data.model.CountryDetails
import retrofit2.Response
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
interface RetrofitService {
@GET("rest/v2/all")
fun fetchCharacterName(): Single&amp;lt;Response&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt;&amp;gt;
companion object {
fun getRetrofitInstance(): RetrofitService {
val retrofit: Retrofit = Retrofit.Builder()
.baseUrl("https://restcountries.eu/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
return retrofit.create(RetrofitService::class.java)}}
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above class is using the retrofit @Get annotation to pull the data from the Url&lt;/p&gt;

&lt;p&gt;CountryDetails.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.data.model
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import java.io.Serializable
data class CountryDetails(
    val id: Int,
    @SerializedName("name")
    @Expose
    val name: String,
    @SerializedName("flag")
    @Expose
    val flag: String,
    val timezones : ArrayList&amp;lt;String&amp;gt;,
    @SerializedName("translations")
    val translations: Translations,
    @SerializedName("languages")
    val language: List&amp;lt;Language&amp;gt;
) : Serializable


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Language.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.data.model
import com.google.gson.annotations.Expose
import com.google.gson.annotations.SerializedName
import java.io.Serializable
data class CountryDetails(
    val id: Int,
    @SerializedName("name")
    @Expose
    val name: String,
    @SerializedName("flag")
    @Expose
    val flag: String,
    val timezones : ArrayList&amp;lt;String&amp;gt;,
    @SerializedName("translations")
    val translations: Translations,
    @SerializedName("languages")
    val language: List&amp;lt;Language&amp;gt;
) : Serializable


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Translations.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.data.model
import java.io.Serializable
data class Translations(
    val de: String,
    val es: String,
    val fr: String,
    val ja: String,
    val it: String,
    val br: String,
    val pt: String,
    val nl: String,
    val hr: String,
    val fa: String
) : Serializable


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above classes are the data class for representing the data structure present in the api also called model class&lt;/p&gt;

&lt;p&gt;CountryDetailsRepository.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.data
import android.util.Log
import androidx.lifecycle.MutableLiveData
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import kulloveth.developer.com.countrydetails.api.RetrofitService
import kulloveth.developer.com.countrydetails.data.model.CountryDetails
import retrofit2.Response
class CountryDetailsRepository {
val countrysLiveData: MutableLiveData&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt; by lazy {
        MutableLiveData&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt;().also {
            fetchCountryDetails()
        }
    }
fun fetchCountryDetails() {
        val api = RetrofitService.getRetrofitInstance().fetchCharacterName()
        api.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(object : SingleObserver&amp;lt;Response&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt;&amp;gt; {
                override fun onSuccess(t: Response&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt;) {
                    countrysLiveData.value = t.body()
                    Log.d("rest", "" + countrysLiveData)
                }
override fun onSubscribe(d: Disposable) {}
override fun onError(e: Throwable) {}})
    }}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;CountrysViewModel.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.countrys
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kulloveth.developer.com.countrydetails.data.CountryDetailsRepository
import kulloveth.developer.com.countrydetails.data.model.CountryDetails
import kulloveth.developer.com.countrydetails.data.model.Language
import kulloveth.developer.com.countrydetails.data.model.Translations
class CountrysViewModel : ViewModel() {
private var countrysLiveData = MutableLiveData&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt;()
    var translationsLiveData = MutableLiveData&amp;lt;Translations&amp;gt;()
    var languageLiveData = MutableLiveData&amp;lt;List&amp;lt;Language&amp;gt;&amp;gt;()
   fun fetchCountrys(): LiveData&amp;lt;List&amp;lt;CountryDetails&amp;gt;&amp;gt; {
        countrysLiveData = CountryDetailsRepository().countrysLiveData
        return countrysLiveData
    }
   fun setTranslations(translations: Translations) {
        translationsLiveData.value = translations
    }
   fun setLanguages(language: List&amp;lt;Language&amp;gt;) {
        languageLiveData.value = language
    }}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above class extends the viewmodel class and contains the data to be observed by the view classes. The methods to take note of here is the setTranslations and setLanguages which am going to use to pass data between CountrysFragment where the data is been setup  to both the TranslationsFragment and the LanguagesFragment where the data is been observed&lt;/p&gt;

&lt;p&gt;MainViewModelFactory.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import kulloveth.developer.com.countrydetails.ui.countrys.CountrysViewModel
class MainViewModelFactory : ViewModelProvider.NewInstanceFactory() {
    override fun &amp;lt;T : ViewModel?&amp;gt; create(modelClass: Class&amp;lt;T&amp;gt;): T {
        return CountrysViewModel() as T
    }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;activity_main.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 
   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:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"&amp;gt;
&amp;lt;fragment
   android:id="@+id/nav_host_fragment"
   android:name="androidx.navigation.fragment.NavHostFragment"
   android:layout_width="0dp"
   android:layout_height="0dp"
   app:layout_constraintLeft_toLeftOf="parent"
   app:layout_constraintRight_toRightOf="parent"
   app:layout_constraintTop_toTopOf="parent"
   app:layout_constraintBottom_toBottomOf="parent"
   app:defaultNavHost="true"
   app:navGraph="@navigation/nav_graph" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Three important points to note in the layout above is android:name which contains the class name of your Nav Host implementation, app:navGraph which associates the navHostFragment with a nav-graph where the user destinations are been specified, app:defaultNavHost="true" which ensures that your NavHostFragment intercepts the system Back button.&lt;/p&gt;

&lt;p&gt;To add a navigation graph to your project, do the following:&lt;br&gt;
In the Project window, right-click on the res directory and select New &amp;gt; Android Resource File. The New Resource File dialog appears.&lt;br&gt;
Type a name in the File name field, such as "nav_graph".&lt;br&gt;
Select Navigation from the Resource type drop-down list, and then click OK. A navigation resource directory will be automatically created in the res directory and there you find you nav-graph&lt;/p&gt;

&lt;p&gt;nav-graph.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
 &amp;lt;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/nav_graph"
 app:startDestination="@id/countrysFragment"&amp;gt;
 &amp;lt;fragment
  android:id="@+id/countrysFragment"
android:name="kulloveth.developer.com.countrydetails.ui.countrys.CountrysFragment"
android:label="fragment_countrys"
tools:layout="@layout/fragment_countrys" &amp;gt;
&amp;lt;action
 android:id="@+id/action_countrysFragment_to_detailsFragment"
 app:destination="@id/detailsFragment" /&amp;gt;
    &amp;lt;/fragment&amp;gt;
    &amp;lt;fragment
        android:id="@+id/detailsFragment"
android:name="kulloveth.developer.com.countrydetails.ui.details.DetailsFragment"
        android:label="fragment_details"
        tools:layout="@layout/fragment_details" &amp;gt;
        &amp;lt;argument
            android:name="countryName"
  app:argType="kulloveth.developer.com.countrydetails.data.model.CountryDetails"
            app:nullable="false" /&amp;gt;
&amp;lt;argument
            android:name="countryFlag"
app:argType="kulloveth.developer.com.countrydetails.data.model.CountryDetails"
            app:nullable="false" /&amp;gt;
&amp;lt;argument
            android:name="timeZone"
  app:argType="kulloveth.developer.com.countrydetails.data.model.CountryDetails"
            app:nullable="false" /&amp;gt;
    &amp;lt;/fragment&amp;gt;
&amp;lt;/navigation&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In the above nav-graph take note of the  tags inside the detailsFragment which contains the details of the data to be received by the fragment. android:name is the String key used to identify a particular data while passing and receiving, app:argType is used to represent what type of data is been passed, app:nullable is used to indicate whether the data can be null or not &lt;/p&gt;

&lt;p&gt;MainActivity.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;fragment_countrys.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;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=".ui.countrys.CountrysFragment"&amp;gt;
    &amp;lt;androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/countryRv" /&amp;gt;
  &amp;lt;/FrameLayout&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;CountrysAdapter.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.countrys
import android.app.Activity
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.ahmadrosid.svgloader.SvgLoader
import kotlinx.android.synthetic.main.rv_list_item.view.*
import kulloveth.developer.com.countrydetails.R
import kulloveth.developer.com.countrydetails.data.model.CountryDetails
class CountrysAdapter : ListAdapter&amp;lt;CountryDetails, CountrysAdapter.MainViewHolder&amp;gt;(
    DiffCallback()
) {
    lateinit var mItemCLicked: ItemCLickedListener
class DiffCallback : DiffUtil.ItemCallback&amp;lt;CountryDetails&amp;gt;() {
        override fun areItemsTheSame(oldItem: CountryDetails, newItem: CountryDetails): Boolean {
            return oldItem.id == newItem.id
        }
override fun areContentsTheSame(oldItem: CountryDetails, newItem: CountryDetails): Boolean {
            return oldItem.id == newItem.id
        }}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder{
        val view = LayoutInflater.from(parent.context).inflate(R.layout.rv_list_item, parent, false)
        return MainViewHolder(view)}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
        holder.bind(getItem(position))
        holder.itemView.setOnClickListener {
            mItemCLicked.let {
                mItemCLicked.onItemClicked(getItem(position))}}}
    fun setUpListener(itemCLicked: ItemCLickedListener){
        mItemCLicked = itemCLicked}
 class MainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(countryDetails: CountryDetails) {
            itemView.country.text = countryDetails.name
            SvgLoader.pluck()
                .with(itemView.context as Activity?)
                .setPlaceHolder(R.mipmap.ic_launcher, R.mipmap.ic_launcher)
                .load(countryDetails.flag, itemView.flag)}}
interface ItemCLickedListener {
        fun onItemClicked(countryDetails: CountryDetails)}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above class is an adapter class used to setup data for the recyclerView present in the CountrysFragment.kt&lt;/p&gt;

&lt;p&gt;rv_list_item.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    app:cardUseCompatPadding="true"&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"&amp;gt;
&amp;lt;ImageView
            android:id="@+id/flag"
            android:layout_width="wrap_content"
            android:layout_height="100dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" /&amp;gt;
&amp;lt;TextView
            android:id="@+id/country"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:textColor="#08141a"
            app:layout_constraintEnd_toEndOf="@id/flag"
            app:layout_constraintStart_toStartOf="@id/flag"
            app:layout_constraintTop_toBottomOf="@id/flag" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;
&amp;lt;/androidx.cardview.widget.CardView&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The recyclerview item layout used for the adapter viewHolder&lt;/p&gt;

&lt;p&gt;CountrysFragment.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.countrys
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavController
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import kotlinx.android.synthetic.main.fragment_countrys.*
import kulloveth.developer.com.countrydetails.R
import kulloveth.developer.com.countrydetails.data.model.CountryDetails
import kulloveth.developer.com.countrydetails.ui.MainViewModelFactory
class CountrysFragment : Fragment() {
 val adapter = CountrysAdapter()
    var navController: NavController? = null
    private val viewModelFactory = MainViewModelFactory()
    private lateinit var viewModel: CountrysViewModel
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        initViewModel()
    }
     override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_countrys, container, false)
    }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        navController = Navigation.findNavController(view)
countryRv.layoutManager = StaggeredGridLayoutManager(3, RecyclerView.VERTICAL)
        countryRv.adapter = adapter
    }
fun initViewModel() {
activity.let {
            viewModel = ViewModelProvider(
                requireActivity(),
                viewModelFactory
            ).get(CountrysViewModel::class.java)
            viewModel.fetchCountrys().observe(this, Observer {
                it.forEach {
                    Log.d("nnnn", "" + it.name)
                }
                adapter.submitList(it)
            })
adapter.setUpListener(object : CountrysAdapter.ItemCLickedListener {
                override fun onItemClicked(countryDetails: CountryDetails) {
                    val bundle = bundleOf(
                        "countryName" to countryDetails.name,
                        "countryFlag" to countryDetails.flag,
                        "timeZone" to countryDetails.timezones
                    )
                    viewModel.setTranslations(countryDetails.translations)
                    viewModel.setLanguages(countryDetails.language)
                    navController?.navigate(
                        R.id.action_countrysFragment_to_detailsFragment,
                        bundle
                    )
                    Log.d("cor", "" + countryDetails.name)}})}}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In the above class, take note of the bundleOf() method where I mapped the countrys name, the countrys flag and the timezone to the respective keys which will be used later to retrieve them in the DetailsFragment,the bundle is passed as a second parameter to the navigate method to carry the data with it while moving to its destination.Also take note of the setTranslations and setLanguages method from the CountrysViewModelclass which is used to set the translations and languages of each countrys and will later be observed from TranslationsFragment and LanguagesFragment respectively.&lt;/p&gt;

&lt;p&gt;fragment_details.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.details.DetailsFragment"&amp;gt;
&amp;lt;ImageView
        android:id="@+id/flag_img"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/country_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="country"
        android:textColor="@color/worldColor"
        android:textSize="24sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="@id/flag_img"
        app:layout_constraintStart_toStartOf="@id/flag_img"
        app:layout_constraintTop_toBottomOf="@id/flag_img" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/timezone_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="TimeZone"
        android:textColor="@color/worldColor"
        app:layout_constraintEnd_toStartOf="@id/timeZone"
        app:layout_constraintStart_toStartOf="@id/flag_img"
        app:layout_constraintTop_toBottomOf="@id/country_text" /&amp;gt;
 &amp;lt;TextView
        android:id="@+id/timeZone"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="UTC"
        android:textColor="@color/worldColor"
        app:layout_constraintEnd_toEndOf="@id/flag_img"
        app:layout_constraintStart_toEndOf="@id/timezone_text"
        app:layout_constraintTop_toBottomOf="@id/country_text" /&amp;gt;
&amp;lt;com.google.android.material.tabs.TabLayout
        android:id="@+id/tabLayout"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/timeZone"
        app:tabBackground="@color/worldColor"
        app:tabIndicatorColor="@android:color/white"
        app:tabTextColor="@android:color/white" /&amp;gt;
&amp;lt;androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tabLayout" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;ViewPagerAdapter.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.details
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import kulloveth.developer.com.countrydetails.ui.details.languages.LanguagesFragment
class ViewPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
    override fun getItemCount(): Int = 2
     override fun createFragment(position: Int): Fragment = when (position) {
     0 -&amp;gt; TranslationsFragment()
        1 -&amp;gt; LanguagesFragment()
        else -&amp;gt; throw IllegalStateException("Invalid adapter position")
    }}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The above class is the adapter class for ViewPager2 present in the DetailsFragment which contains two other fragments.&lt;/p&gt;

&lt;p&gt;DetailsFragment.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.details
import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.ahmadrosid.svgloader.SvgLoader
import com.google.android.material.tabs.TabLayoutMediator
import kotlinx.android.synthetic.main.fragment_details.*
import kulloveth.developer.com.countrydetails.R
class DetailsFragment : Fragment() {
private lateinit var viewPagerAdapter: ViewPagerAdapter
    var countryName: String? = null
    private var countryFlag: String? = null
    var timeZon: ArrayList&amp;lt;String&amp;gt;? = null
 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        countryName = arguments?.getString("countryName")
        countryFlag = arguments?.getString("countryFlag")
        timeZon = arguments?.getStringArrayList("timeZone")
    }
override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_details, container, false)
    }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        Log.d("corn", "" + countryName)
country_text.text = countryName
        timeZon?.forEach {
            timeZone.text = it
        }
SvgLoader.pluck()
            .with(context as Activity?)
            .setPlaceHolder(R.mipmap.ic_launcher, R.mipmap.ic_launcher)
            .load(countryFlag, flag_img)
viewPagerAdapter = ViewPagerAdapter(this)
        pager.adapter = viewPagerAdapter
        TabLayoutMediator(tabLayout, pager) { tab, position -&amp;gt;
            when (position) {
                0 -&amp;gt; tab.text = "Translation"
                1 -&amp;gt; tab.text = "Languages"
            }}.attach()
   }}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In the code above observe the onCreate() method where the data is been received by passing its type of data with its key to arguments. countrys name and flag are both strings so getString method is used, timezone is a list so getStringArrayList is used.&lt;/p&gt;

&lt;p&gt;fragment_translations.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout 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"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ui.details.TranslationsFragment"&amp;gt;
&amp;lt;TextView
        android:id="@+id/translation_tv"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:gravity="center"
        android:text="Translations"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/german_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="German"
        app:layout_constraintEnd_toStartOf="@id/germanTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/translation_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/germanTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="GermanTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/german_tv"
        app:layout_constraintTop_toBottomOf="@id/translation_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/spanish_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Spanish"
        app:layout_constraintEnd_toStartOf="@id/spanishTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/german_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/spanishTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="SpanishTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/spanish_tv"
        app:layout_constraintTop_toBottomOf="@id/german_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/french_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="French"
        app:layout_constraintEnd_toStartOf="@id/frenchTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/spanish_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/frenchTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="FrenchTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/french_tv"
        app:layout_constraintTop_toBottomOf="@id/spanish_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/japan_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Japan"
        app:layout_constraintEnd_toStartOf="@id/japanTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/french_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/japanTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="JapanTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/japan_tv"
        app:layout_constraintTop_toBottomOf="@id/french_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/italian_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Italian"
        app:layout_constraintEnd_toStartOf="@id/italianTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/japan_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/italianTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="ItalianTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/italian_tv"
        app:layout_constraintTop_toBottomOf="@id/japan_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/persian_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="Persian"
        app:layout_constraintEnd_toStartOf="@id/persianTrans"
        app:layout_constraintStart_toStartOf="@id/translation_tv"
        app:layout_constraintTop_toBottomOf="@id/italian_tv" /&amp;gt;
&amp;lt;TextView
        android:id="@+id/persianTrans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="PersianTrans"
        app:layout_constraintEnd_toEndOf="@id/translation_tv"
        app:layout_constraintStart_toEndOf="@id/persian_tv"
        app:layout_constraintTop_toBottomOf="@id/italian_tv" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;TranslationsFragment.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.details
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import kotlinx.android.synthetic.main.fragment_translations.*
import kulloveth.developer.com.countrydetails.R
import kulloveth.developer.com.countrydetails.ui.countrys.CountrysViewModel
class TranslationsFragment : Fragment() {
private val viewModel: CountrysViewModel by activityViewModels()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_translations, container,false)}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
 viewModel.translationsLiveData.observe(viewLifecycleOwner, Observer {
            germanTrans.text = it.de
            spanishTrans.text = it.es
            frenchTrans.text = it.fr
            japanTrans.text = it.ja
            italianTrans.text = it.it
            persianTrans.text = it.fa})}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;TranslationsFragment is one of the fragments on the viewpager, the translations which was created in CountrysViewModel.kt and setup in CountrysFragment.kt is now been observed in this fragment with the code viewmodel.translationsLiveData.observe{}. &lt;/p&gt;

&lt;p&gt;fragment_languages.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;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=".ui.details.languages.LanguagesFragment"&amp;gt;
  &amp;lt;androidx.recyclerview.widget.RecyclerView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:id="@+id/recyclerView"/&amp;gt;
&amp;lt;/FrameLayout&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;languages_item.xml&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"&amp;gt;
&amp;lt;TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/name_tv"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintEnd_toStartOf="@id/name"
        app:layout_constraintStart_toStartOf="parent"
        android:text="Name" /&amp;gt;
    &amp;lt;TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/name"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_margin="8dp"
        app:layout_constraintStart_toEndOf="@id/name_tv"
        android:text="@string/hello_blank_fragment" /&amp;gt;
&amp;lt;TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/nativeName_tv"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@id/name_tv"
        app:layout_constraintStart_toStartOf="parent"
        android:text="Native name:" /&amp;gt;
&amp;lt;TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/nativeName"
        android:layout_margin="8dp"
        app:layout_constraintTop_toBottomOf="@id/name"
        app:layout_constraintStart_toEndOf="@id/nativeName_tv"
        android:text="@string/hello_blank_fragment" /&amp;gt;
&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;LanguageAdapter.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.details.languages
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.languages_item.view.*
import kulloveth.developer.com.countrydetails.R
import kulloveth.developer.com.countrydetails.data.model.Language
class LanguageAdapter : ListAdapter&amp;lt;Language, LanguageAdapter.MainViewHolder&amp;gt;(
    DiffCallback()) {
 class DiffCallback : DiffUtil.ItemCallback&amp;lt;Language&amp;gt;() {
        override fun areItemsTheSame(oldItem: Language, newItem: Language): Boolean { return oldItem.id == newItem.id}
override fun areContentsTheSame(oldItem: Language, newItem: Language): Boolean {
            return oldItem.id == newItem.id}}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {val view = LayoutInflater.from(parent.context).inflate(R.layout.languages_item, parent, false)
        return MainViewHolder(
            view)}
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
        holder.bind(getItem(position))}
 class MainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(language: Language) {
            itemView.name.text = language.name
            itemView.nativeName.text = language.nativeName
}}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The LanguagesFragment uses a recyclerview and the above is its adapter&lt;/p&gt;

&lt;p&gt;LanguagesFragment.kt&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package kulloveth.developer.com.countrydetails.ui.details.languages
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.fragment_languages.*
import kulloveth.developer.com.countrydetails.R
import kulloveth.developer.com.countrydetails.ui.countrys.CountrysViewModel
class LanguagesFragment : Fragment() {
val adapter = LanguageAdapter()
    val viewModel: CountrysViewModel by activityViewModels()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_languages, container, false)
    }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        recyclerView.layoutManager = LinearLayoutManager(requireActivity())
        recyclerView.adapter = adapter
        recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
activity.let {
            viewModel.languageLiveData.observe(viewLifecycleOwner, Observer {
                adapter.submitList(it)
            })}}}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;In the above code the languageLiveData is also been observed to access the language list which was also setup in the CountrysFragment.&lt;br&gt;
 Also note that in each of these fragment that is receiving the data using viewmodel, the viewmodel class was initialized using activityViewModels() this is to ensure that these fragments are retrieving the activity that contains them.&lt;/p&gt;

&lt;p&gt;Summary&lt;br&gt;
In this post we talked about navigation component, how you can pass data with Bundle between fragments when using the component and also how you can pass data between fragments using viewmodel. I hope this helps you out in solving related problems, you can drop your feedback and questions in the comment section &lt;a href="https://www.twitter.com/kulloveth" rel="noopener noreferrer"&gt;&lt;code&gt;LOVETH&lt;/code&gt;&lt;/a&gt; &lt;br&gt;
The github link to the full code is &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/kulloveth" rel="noopener noreferrer"&gt;
        kulloveth
      &lt;/a&gt; / &lt;a href="https://github.com/kulloveth/CountryDetails" rel="noopener noreferrer"&gt;
        CountryDetails
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      List of Countries and all possible information about them 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;CountryDetails&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;This project is based on an Api from restcountries.eu to get List of Countries and all possible information about them&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;IDE-Integrated Development Environment&lt;/h1&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Android Studio-
Find link on how you can setup Android studio &lt;a href="https://developer.android.com/studio?pkg=studio" rel="nofollow noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Libraries used and their documentation&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Retrofit2 &lt;a href="http://square.github.io/retrofit/2.x/retrofit/" rel="nofollow noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Rxjava2 &lt;a href="https://github.com/ReactiveX/RxJava/" rel="noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RxAndroid &lt;a href="https://github.com/ReactiveX/RxAndroid" rel="noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SvgLoader &lt;a href="https://github.com/ar-android/AndroidSvgLoader" rel="noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NavComponents &lt;a href="https://developer.android.com/guide/navigation" rel="nofollow noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Screenshot&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/CountryDetails/blob/master/app/screenshots/navComponents.PNG"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FCountryDetails%2Fraw%2Fmaster%2Fapp%2Fscreenshots%2FnavComponents.PNG" alt="nav_graph"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ViewModel &lt;a href="https://developer.android.com/topic/libraries/architecture/viewmodel" rel="nofollow noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LiveData &lt;a href="https://developer.android.com/topic/libraries/architecture/livedata" rel="nofollow noopener noreferrer"&gt;Doc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Screenshots&lt;/h1&gt;

&lt;/div&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/CountryDetails/blob/master/app/screenshots/countrys.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FCountryDetails%2Fraw%2Fmaster%2Fapp%2Fscreenshots%2Fcountrys.png" alt="countrys"&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;/td&gt;
 &lt;td&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/CountryDetails/blob/master/app/screenshots/translation.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FCountryDetails%2Fraw%2Fmaster%2Fapp%2Fscreenshots%2Ftranslation.png" alt="translation"&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;/td&gt;
   &lt;td&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/kulloveth/CountryDetails/blob/master/app/screenshots/language.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fkulloveth%2FCountryDetails%2Fraw%2Fmaster%2Fapp%2Fscreenshots%2Flanguage.png" alt="languages"&gt;&lt;/a&gt;&lt;/p&gt;
  &lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/kulloveth/CountryDetails" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
 

</description>
      <category>kotlin</category>
      <category>navigationcomponent</category>
      <category>viewmodel</category>
      <category>fragment</category>
    </item>
  </channel>
</rss>
