Let's say my application has 4 activities (A -> B -> C -> D) when going forward, activity A pass a data value called category to activity B and then activity B pass category and also and data value called level and both used in activity D to get data from firebase realtime database. but when the user goes back to activity C and tries to go again to D data passed from A (category) is now null. so data can't retrieve from firebase. my question is how can I solve this situation.? "I pass data using Intent by putting extras." are there any other ways?
you could use startactivityforresult and set a result intent which includes the data you want to pass back
https://developer.android.com/training/basics/intents/result
You can use a shared ViewModel. Viewmodels are lifecycle-aware components. Data in ViewModels does not disappear when activity changes
Save your data (category,level) in ViewModel like this :
private val _categoryValue =
MutableLiveData<CategoryType>()
val categoryValue:
LiveData<ResponseState<CategoryType> =
_categoryValue
And update your data in activity like this:
vm.categoryValue.value = newCategoryValue
If you are using binding etc. observe and do stuff with your data like this:
vm.deleteFileResponse.observe(this){ category ->
//Do stuff
}
Related
I am searching for postSticky() method replacement. It is being used for simple passing value to previous Fragment, but thing is that I am using BackStackUtil for navigation so instance() method is being called when returning only if stack gets cleared somehow before getting back.Previous Fragment is holding List of items, when next Fragment can modify picked item and yet another one can do something else so it is chain of sticky events when each of these is being passed to previous Fragment. App structure won't let me to apply Coordinator pattern at current stage and also I don't want to attach Bundle to Fragments kept on stack. I was looking for solutions but I couldn't find any. I also don't want to store values in some static fields or SharedPreferences/Data storage.I was thinking about shared ViewModel but I don't really like this idea to be honest, so I would appreciate any ideas or just confirmation if shared VM is the only/best way.
Do you have any other ideas?
In your A fragment, before navigating to B fragment, listen to savedStateHandle:
findNavController()
.currentBackStackEntry
?.savedStateHandle?.getLiveData<Bundle>("DATA_KEY")
?.observe(viewLifecycleOwner) { result ->
// Result from fragment B
}
In your B fragment, before navigating back, set the data to pass to A fragment:
findNavController()
.previousBackStackEntry
?.savedStateHandle
?.set("DATA_KEY", result)
You can remove the observer using:
findNavController()
.currentBackStackEntry
?.savedStateHandle?.remove<Bundle>
Note that here the passed type is Bundle (the type in getLiveData<Bundle>) but you can use any type you want.
My aim is to list data in a fragment. For example, I want to list X when going from activity A, list Y data when going from activity B. How can I check this?
if(shouldPaginate){
// If I came here from activity a
viewModel.getUsBreakingNews("tr")
isScrooling=false
//If I came here from activity b
viewModel.getUsBreakingNews("us")
isScrooling=false
}
Basically you can define arguments for the destination-fragment and pass it to the ViewModel. Then in the ViewModel you check the variable (where the user came from) and execute the particular code. Check this out if you are using Jetpack Compose https://developer.android.com/guide/navigation/navigation-pass-data.
In my activity#1 I create my viewModel like this:
val viewViewModelProvider = ViewModelProviders.of(this)
bluetoothPageViewModel = viewViewModelProvider.get(BluetoothPageViewModel::class.java)
Nice it's work fine.
But I need when forward to another activity (activity#2) to destroy or clear all data in bluetoothPageViewModel.Because when I return (press back button) to activity#1 I need all data in bluetoothPageViewModel to be in init values.
Is it possible.
let sat I have some destinations like this
from fragment A --> to fragment B --> to fragment C
I can use Safe Args to pass data from fragment A to fragment B. and also using safe args from fragment B to fragment C.
what if I want to bring a string that generated in fragment C back to fragment B or to fragment A ?
to navigate from fragment C to fragment B, I use code:
Navigation.findNavController(fragmentView).navigateUp()
and to navigate from fragment C to fragment A, I use code:
Navigation.findNavController(view).popBackStack(R.id.fragmentA, false)
so how to pass a value back to previous fragment destination ? do I have to make an action back from fragment C to fragment B and pass the value through safe args ?
really need your help. Thank you very much :)
You can use the below snippet for sending data to previous fragment.
Fragment A observing the data:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe(viewLifecycleOwner) { data ->
// Do something with the data.
}
}
Fragment B sending the data:
findNavController().previousBackStackEntry?.savedStateHandle?.set("key", data)
findNavController().popBackStack()
I also wrote extensions for this.
fun <T : Any> Fragment.setBackStackData(key: String,data : T, doBack : Boolean = true) {
findNavController().previousBackStackEntry?.savedStateHandle?.set(key, data)
if(doBack)
findNavController().popBackStack()
}
fun <T : Any> Fragment.getBackStackData(key: String, result: (T) -> (Unit)) {
findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)?.observe(viewLifecycleOwner) {
result(it)
}
}
Usage:
Fragment A:
getBackStackData<String>("key") { data ->
// Do something with the data.
}
Fragment B:
setBackStackData("key",data)
Note: I am using String as data. You may use any other type of variable.
Note: If you are going to use a Class as data, don't forget to add #Parcelize annotation and extend Parcelable.
i have just came across the same problem and have several options i can propose (currently considering myself which ones is best for my use case).
create a navigation action from fragment C back to fragment A. note that if that's all you do, your stack will now be: A-B-C-A. to avoid this, you can use popUpTo and PopUpToInclusive. see the documentation.
the disadvantage of this method is that fragment A will be cleared no matter what, and would have to re-load (and fetch all its' data again).
use a shared ViewModel between for your fragments, and observe the required value with LiveData. in this scenario, fragment C will update a LiveData value in the shared ViewModel, which fragment A observes. now when you go back to fragment A, your observer will be triggered with the value set by fragment C.
similar to option 2, if you are using an external data source (like a Room database) you can observe changes using LiveData in fragment A. all fragment C has to do in this case is update the data source, and fragment A will automatically be updated through the LiveData
I Faced some problem with the relevance of the data in application.
In our app we have 1 activity and many fragments.
For example, we have 1 Fragment with list of User and there button for like(with 3 states: none/like/favorite).
In next page we have full description of User and like button too.
If we press like button in userList, there will be no problem and in details screen we see correct like state. But if we click like button in User details and go back to list, there still past data here.
We can't use activityForResult because fragment.
We using rx, maybe there some way to easy resolve this problem?
In order to reuse the Fragment UI components, you should build each as a completely self-contained, modular component that defines its own layout and behavior. Each Fragment then can communicate data to the Activity, and the Activity can also send data to Fragments.
In your case, the Activity can hold the actual data. When user modifies something in the details Fragment, the Fragment should intimate that to the Activity. The Activity, then in turn should inform the list Fragment.
Actually, you can use the onActivityResult() approach with fragments. I've seen this pattern a couple of times in different projects, up to you to decide whether it suits you.
Assuming your left fragment is AFragment and your right fragment is BFragment, here's the idea.
(1) In AFragment, set a target fragment prior to commiting a transaction from A to B:
Fragment bFragment = new BFragment();
bFragment.setTargetFragment(this, 42); // `this` is your AFragment instance
// transact here
(2) Provide onActivityResult():
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// your logic here
}
(3) In BFragment, prepare your data in onDetach() and call onActivityResult() on your target.
#Override
public void onDetach() {
super.onDetach();
if (getTargetFragment() != null) {
Intent data = new Intent();
// pass your data here
getTargetFragment().onActivityResult(getTargetRequestCode(), Activity.RESULT_OK, data);
}
}
This way, whenever BFragment pops back, your AFragment has a chance to handle its data.
The way I deal with this situation in my app is to use a central "repository" of SharedPreferences to store serialized data across multiple screens. Then every time a new Activity (or in your case, Fragment) gets created or resumed, it reaches into the serialized data repo to populate its shared data.
This is how I would imagine this would work in your case. To use your "user" example, I would create a SharedPreferences file to store my list of user data objects. So I would have a class that stores everything I need to know about a user -- their info, their "like" status, etc.
Assuming I have the users in a List somewhere, I would just serialize that with Gson, and save the serialized data into the SharedPreferences I created. Then, each Fragment can just poll the serialized data to get the current state of the user. If some changes are made, each Fragment would save the data back to the same SharedPreferences file so that other Fragments could then use it to instantiate their views.
Here's some pseudo code...
//Some kind of data class for User
class MyUser {
String name;
String location;
String likeStatus;
//... whatever else
}
Then, assuming you have a List of users somewhere, I would serialize that List and store it in a global SharedPreferences file like this...
//ArrayList<MyUser> users <- defined somewhere
Gson gson = new Gson();
String usersJson = gson.toJson(users);
SharedPreferences userPrefs = getSharedPreferences("users_storage", MODE_PRIVATE);
SharedPreferences.Editor editor = userPrefs.edit();
editor.putString("users_data_key", usersJson);
editor.apply();
Then when a new Fragment is loaded and it needs to know the current state of the users, it simply opens the SharedPreferences file and gets the current state of everything like this...
SharedPreferences userPrefs = getSharedPreferences("users_storage", MODE_PRIVATE);
String usersJson = userPrefs.getString("users_data_key", null);
Type collectionType = new TypeToken<ArrayList<MyUser>>(){}.getType();
ArrayList<MyUser> users = gson.fromJson(usersJson, collectionType);
Just to be clear, this approach would be all done in your Activity, and when you were starting a new Fragment, you would simply pass in the data it needs to instantiate correctly. Then, if a Fragment needs to save the data, it would simply call a method of your Activity and pass it the data that needs to be saved, and your Activity would execute the code above to save it.