Sharing data between fragment and its parent activity using ViewModel - android

I would like to ask if it's correct to share the same ViewModel between Fragment and its Activity.I have UserDetailActivity and UserDetailFragment. Can I use the same ViewModel to display detail data of a user in the UserDetailActivity and UserDetailFragment or is there better approach.

Yes you can pass ViewModal object from a Activity to Fragment or vice-versa by implementing Parcelable into ViewModal class and can share object with fragment setArguments() method.

I am not using MVVM but I think its the same with MVP, I use the same Presenter(ViewModel in your case) with my Activity and its child Fragment. It make since because a Fragment is literally a fragment of an Activity. There might be some special cases where you really want to separate viewModel of Fragment and Activity, but most of the time, they share. About the initialization, dont pass your viewmodel directly, you can use dagger and inject it.

Related

Communicating between Activities and Fragments when using Navigation Architecture Component

I want to let the host Activity know when something happens in a Fragment. Traditionally, I would have an interface with a callback that the Fragment can call, but now we are ofc using the navigation architecture component.
Is there a way to pass a reference to the host activity down to the fragments or how would I otherwise solve the "Communication between activities and fragments" situation?
Thanks!
You can use the LiveData data holder class for such purposes.
Here is an article explaining both Fragment <--> Fragment communication and Activity <--> Fragment communication.
you can have a shared ViewModel between your activity and all of your fragments. and use liveData in that viewModel. so when something happens in the fragments you change the liveData and you observe the liveData in the activity
You can use a Shared ViewModel (https://developer.android.com/topic/libraries/architecture/viewmodel#sharing)
It would be alive while the activity is and all child fragments can access it.
A LiveData in a Shared ViewModel, for example, could be listened to in all the fragments. If one of those fragments change the data all others would get that change.

ViewModel as parameter on fragment constructor

I Have Fragment A that calls Fragment B or Fragment C.
B and C uses a ViewModel provided by A.
For while, I'm passing the View as parameter on constructor:
FragmentB(val viewModel: ViewModel)
FragmentC(val viewModel: viewModel)
But this way, not is a good solution, because the app is crashing when try to reopen the fragment.
Could not instantiate the fragment
What the best way to do this?
I thought create a newInstance method and pass the viewModel as argument on Bundle, but how I would transform this viewModel in Parcelable or Serializable?
You are probably trying to create custom Fragment constructor which is not allowed in Android.
I assume your ViewModel was created inside of fragment A using method
ViewModelProviders.of(this)
ViewModel created this way should be used only inside of this fragment(It won't crash if you i.e. pass it through Object or Singleton but it's not supposed to be used that way).
Better solution is to put all of these fragments A, B, C inside one activity.
This way you can call
ViewModelProviders.of(requireActivity())
and get the same instance of the ViewModel in all of the fragments. So if you get the ViewModel using this method in your fragment A, put there some values and then replace fragment A with fragment B in the same activity, you can call ViewModelProviders.of(requireActivity()) again get access to the same instance of your ViewModel and retrieve the values.

Which is the best way to send data between fragments? ViewModel or Destination Arguments?

Imagine that I have two fragments:
FirstFragment
SecondFragment
User will prompt some data in FirstFragment, and I'll like to share some of that data to SecondFragment. I'm using Android's ViewModel and Jetpack's Navigation. Which is the best way to pass data?
ViewModel
In the case of the viewModel, if I instantiate the viewModel like this:
MyViewModel viewModel = ViewModelProviders.of(getActivity()).get(MyViewModel.class);
in every fragment, I could create a method called setSharedData and call it from FirstFragment, and create methods to getSpecificData and call them as needed in my SecondFragment.
Destination arguments
If I use Jetpack's Navigation I could use destination arguments with safe-args to share data, setting it in the actions between Fragments.
What option do you think is the best? Do you think those are different solutions that suit better for different situations? In that case, which method should I use in which situation?
I think the view model approach is useful when you want using the shared data multiple times but if you only need the shared data for initiating, i recommend to use the destination argument. Because persisting the data in the view model uses of memory and it isn't necessary.

Is it possible to call ViewModel methods from fragments through an Activity or is it a bad practice?

I have an Activity and 4 fragments in it. At first I wanted to do for each fragment of the ViewModel. But the situation is such that I also need ViewModel for Activity. I want to know if it would be an error to make the ViewModel just for the Activity and call the necessary methods from the fragments using getActivity? For example, call getActivity().myViewModel.callMethod() at fragment? Wouldn't this approach be wrong?
Yes, you can use the ViewModel of the Activity at a fragment, but not like that getActivity().myViewModel.callMethod(). It should be like this
YourViewModel viewModel = ViewModelProviders.of(getActivity()).get(YourViewModel.class);
And it's a good practice to share data between fragments.
Official doc of Google says
That way, when the fragments each get the ViewModelProvider, they
receive the same SharedViewModel instance, which is scoped to this
activity.
This approach offers the following benefits:
The activity does not need to do anything, or know anything about this
communication.
Fragments don't need to know about each other besides
the SharedViewModel contract. If one of the fragments disappears, the
other one keeps working as usual.
Each fragment has its own lifecycle,
and is not affected by the lifecycle of the other one. If one fragment
replaces the other one, the UI continues to work without any problems.

Better way to pass data between Fragments Navigation Component?

Passing data between Fragments in Navigation Component is easy. Say going from A to B you just set arguments with SafeArgs and you are done.
But, it gets tricky when passing data from B back to A.
According to documentation, we can use SharedViewModel which is works well. But I am looking for better way of passing data back to A from B.
The problem of using SharedViewModel is, you have to create SharedViewModel for every fragment pair that you need to pass data.
Any suggestions? If any annotation-processing method you can think about, you are more than welcome to recommend.
If you do not want to use SharedViewModel way, you can follow the next approach:
1- Define a delegate for your Details Fragment. (This delegate have to implement Serializable or Parcelable:
interface DetailsFragmentDelegate: Serializable {
fun onSomething1(someData1: SomeData1)
fun onSomething2(someData2: SomeData2)
}
2- Add the delegate to your Details Fragment arguments in nav_graph.xml
3- Pass the delegate to your Details Fragment when navigating to its destination by your Base Fragment:
findNavController().navigate(
BaseFragmentDirections.actionBaseFragmentToDetailsFragment(
object: DetailsFragmentDelegate {
// override delegate methods
}
)
)
4- Get the delegate argument in Details Fragment and pass the data back wherever you need:
....
delegate.onSomething1(data1)
....
delegate.onSomething2(data2)
....
I am not sure whether there is a better way or not, but it's working...
You don't need to create a ViewModel per Fragment pair. What I am doing is creating a ViewModel per Fragment. Each ViewModel would have a map[Class[Fragment], Any] named mailBox.
Each Fragment will define a FragmentResult type which is different per Fragment class.
In the child Fragment onBackPressedHandler, before pop-up, fetch the parent ViewModel from the Activity and put your result in the mailBox for your class. You will need a ViewModel class for that. See below.
The parent Fragment needs to pass it's ViewModel.class to the child Fragment, before launching it.
When the Parent Fragment is re-started after popping up the child from the stack. Get the mailBox map from it's ViewModel, check if there is a key with value from the expected FragmentChild::class. If so, then cast to the desired type.
The parent Fragment ViewModel needs to save who was the last child it launched.
I am using an callback interface for this. So i have created an interface with some methods. I implemented that interface 'A' and then call if from 'B'. Very easy and works great.

Categories

Resources