I have simple registration form. When I enter data and change configuration the data is lost. I use ViewModel in my project and official documentation says ViewModel can handle orientation change automatically but it does not happen. How i suppose to store data with SaveState or I made a mistake in ViewModel?
Fragment code
class StartFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding: StartFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.start_fragment, container, false)
val application = requireNotNull(this.activity).application
val dataSource = UsersDatabase.getInstance(application).usersDatabaseDao
val vm: SavedStateHandle by viewModels()
val viewModelFactory = StartFragmentViewModelFactory(dataSource, application)
val startFragmentViewModel =
ViewModelProvider(
this, viewModelFactory).get(StartFragmentViewModel::class.java)
binding.startFragmentViewModel = startFragmentViewModel
binding.lifecycleOwner = this
binding.start.setOnClickListener {
findNavController().navigate(
StartFragmentDirections
.actionStartFragmentToWebViewFragment())
startFragmentViewModel.doneNavigation()
}
return binding.root
}
}
ViewModel
class StartFragmentViewModel(
val database: UsersDatabaseDao,
application: Application
) : AndroidViewModel(application) {
private var viewModelJob = Job()
override fun onCleared() {
super.onCleared()
viewModelJob.cancel()
}
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
private var user1 = MutableLiveData<User?>()
private val _navigateToWebView = MutableLiveData<User>()
val navigateToWebView: LiveData<User>
get() = _navigateToWebView
fun doneNavigation() {
_navigateToWebView.value = null
uiScope.launch {
val user = User()
insert(user)
}
}
private suspend fun insert(user: User) {
withContext(Dispatchers.IO) {
database.insert(user)
}
}
}
ViewModelFactory
class StartFragmentViewModelFactory (
private val dataSource: UsersDatabaseDao,
private val application: Application
) : ViewModelProvider.Factory {
@Suppress("unchecked_cast")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(StartFragmentViewModel::class.java)) {
return StartFragmentViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
start_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="startFragmentViewModel"
type="com.example.leadsdoittest.StartFragmentViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/margin_start"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_begin="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/margin_end"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:layout_constraintGuide_end="16dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/margin_top"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_name"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="@id/margin_end"
app:layout_constraintStart_toStartOf="@id/margin_start"
app:layout_constraintTop_toTopOf="@id/margin_top">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/name" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_phone"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="@id/margin_end"
app:layout_constraintStart_toStartOf="@id/margin_start"
app:layout_constraintTop_toBottomOf="@id/input_name">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="phone"
android:hint="@string/phone_number" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/input_email"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="@id/margin_end"
app:layout_constraintStart_toStartOf="@id/margin_start"
app:layout_constraintTop_toBottomOf="@id/input_phone">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/email" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="@string/start"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="@id/margin_end"
app:layout_constraintStart_toStartOf="@id/margin_start"
app:layout_constraintTop_toBottomOf="@+id/input_email" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
View state is handled by the framework so you don't need to implement it yourself. However the saved state depends on having unique IDs on each view, so try adding an ID to each TextInputEditText
.