icerockdev / moko-mvvm

Model-View-ViewModel architecture components for mobile (android & ios) Kotlin Multiplatform development
https://moko.icerock.dev/
Apache License 2.0
1.02k stars 95 forks source link

trying to share android ViewModels (+Databinding and Navhostfragment) #84

Closed hlnstepanova closed 3 years ago

hlnstepanova commented 3 years ago

Hi Alex,

I'm trying to rewrite my android ViewModels into shared ViewModels, but cannot manage to get it working. I started with LoginViewModel, which is similar to the one in samples. But in my app I use MainActivity with NavHostFragment and Databinding for navigation drawer. Here is my code. Unfortunately I cannot share the project, but I made gists for all relevant files.

MainActivity.kt activity_main.xml nav_header.xml LoginModel.kt BaseFragment.kt LoginFragment.kt fragment_login.xml

When I clean and build the project, it builds successfully and runs in the emulator.

Then, I see my main activity with login fragment and try to login. When clicking on the login button, LoginModel seems to dispatch the login event which should redirect to the next screen, but nothing happens. I assume I'm doing something wrong when binding the event listener. I was also not sure, whether I have to implement MvvmEventsFragment instead of Fragment() or how I could do it in this case.

 <Button
                android:id="@+id/loginBtn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/login"
                android:onClick="@{() -> viewModel.onLogin()}"
                android:layout_marginVertical="@dimen/large_margin"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/LlLogin" />
fun onLogin() {
        val user = User(username.value, password.value)

        // regardless of login type (cloud or local), validate username and password
        validateUserData(user)?.let {
            eventsDispatcher.dispatchEvent { showError(it) }
            return
        }

        // if cloud login, try login with api
        if (cloudLogin.value) {
            viewModelScope.launch {
                startLoading()
                login(user)?.let { errorString ->
                    eventsDispatcher.dispatchEvent { showError(errorString) }
                    stopLoading()
                    return@launch
                }
                eventsDispatcher.dispatchEvent { navigateToDeviceList(username.value)}
                setLogged(true)
            }
        } else {
            // if local login, validate ip
            validateIpData(url.value)?.let {
                eventsDispatcher.dispatchEvent { showError(it) }
                return
            }
            eventsDispatcher.dispatchEvent { navigateToFavouritesList(url.value)}
            setLogged(true)
        }

    }

I would really appreciate any help.

hlnstepanova commented 3 years ago

I managed to go to the next screen by getting rid of BaseFragment and using MvvmFragment for LoginFragment, as well as changing some parts in MainActivity and LoginModel LoginFragment MainActivity LoginModel

Alex009 commented 3 years ago

hi! sorry for late reply. if problem actual reproducer project will be very helpful. as i understand you try to use EventsDispatcher with simple Fragment, without extends MvvmEventsFragment. to use EventsDispatcher you should bind to lifecycle at your Fragment. as here - https://github.com/icerockdev/moko-mvvm#android-1 or here - https://github.com/icerockdev/moko-mvvm/blob/master/mvvm-databinding/src/main/kotlin/dev/icerock/moko/mvvm/MvvmEventsFragment.kt#L18

hlnstepanova commented 3 years ago

Using MvvmEventsFragment was the solution