I found the question but does not have solution in code
I want to have data when backpress/manual back happens. I am using navigateUp()
to go back. How
You should use static variables/companion objects, because it is better than shared viewmodel as it is not simple/nice architecture. As it it not straightforward, I think it is the best way.
To navigateUp From FragmentB to FragmentA
FragmentB:
isBackpressed = true
findNavController().navigateUp()
FragmentA:
onViewCreated() {
// todo
if(isBackpressed) {
isBackpressed = false
// do whatever you want
}
}
According to developer.android.com, you can use common for fragments where you want to share data ViewModel using their activity scope.
Here are steps:
class SharedViewModel : ViewModel() {
val dataToShare = MutableLiveData<String>()
fun updateData(data: String) {
dataToShare.value = data
}
}
class Fragment1 : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
viewModel.dataToShare.observe(this, Observer<String> { dataFromFragment2 ->
// do something with data
})
}
}
Fragment2
and if you're handling navigation properly, now, you should be able to receive data changes on Fragment1
:class Fragment2 : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
updateDataButton.setOnClickListener { v ->
viewModel.updateData("New data for fragment1")
}
}
}
I hope answer helps.
You can use NavigationResult library. Basically it's startActivityForResult
but for Fragments in Navigation component.
You can just call
findNavController().navigate(R.id.fragment1, args)
where args is your bundle. In fragment1, fetch the data from the arguments
You can use the following simple implementation to pass data after popBackStack() with Android Navigation Component. This approach is based on official Google documentation
You just need to extend your fragments with BaseFragment and use the following methods:
//to start Fragment2 for result
startFragmentForResult(actionId: Int)
//to perform pop back stack in Fragment2
popBackStackWithResult(result: Any)
//to observe your result in Fragment1
onFragmentResult(result: Any)
Add SharedViewModel class
class SharedViewModel : ViewModel() {
val sharedData = MutableLiveData<Any>()
fun share(obj: Any) {
sharedData.value = obj
}
}
Add BaseFragment class
import androidx.fragment.app.Fragment
abstract class BaseFragment : Fragment() {
protected var rootView: View? = null
private var sharedViewModel: SharedViewModel? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if (rootView == null) {
rootView = inflater.inflate(getLayoutId(), container, false)
initSharedViewModel()
}
return rootView
}
abstract fun getLayoutId()
open fun onFragmentResult(result: Any){
//TODO implement in descendants
}
protected fun startFragmentForResult(actionId: Int){
findNavController().navigate(actionId)
}
protected fun popBackStackWithResult(result: Any){
findNavController().popBackStack()
sharedViewModel?.share(result)
}
private fun initSharedViewModel() {
sharedViewModel = ViewModelProvider(activity!!).get(SharedViewModel::class.java)
sharedViewModel?.sharedData?.observe(activity!!, object : Observer<Any> {
override fun onChanged(params: Any) {
onFragmentResult(params)
}
})
}
}
Add Fragment1
class Fragment1 : BaseFragment(){
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
some_btn.setOnClickListener{
startFragmentForResult(R.id.action_fragment1_to_fragment2)
}
}
override fun onFragmentResult(result: Any) {
if(result is AnyYourClass){
//PERFECT! DO SOMETHING WITH YOUR RESULT
}
}
}
Add Fragment2
class Fragment2 : BaseFragment(){
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
some_btn.setOnClickListener{
popBackStackWithResult(AnyYourClass())
}
}
}
Cheers ;)
To pop destinations when navigating from one destination to another, add an app:popUpTo
attribute to the associated <action>
element.
To navigate from fargment2 to Fragment1 with arguments, specify in the navigation graph the action
of the caller fragment and the arguments
of the destination fragment :
<fragment
android:id="@+id/fragment2"
android:name="com.example.myapplication.Fragment2"
android:label="fragment_2"
tools:layout="@layout/fragment_2">
<action
android:id="@+id/action_2_to_1"
app:destination="@id/fragment1"
app:popUpTo="@+id/fragment1"/>
</fragment>
<fragment
android:id="@+id/fragment1"
android:name="com.example.myapplication.Fragment1"
android:label="fragment_1"
tools:layout="@layout/fragment_1">
<argument
android:name="someArgument"
app:argType="string"
app:nullable="false"
android:defaultValue="Hello Word"/>
</fragment>
In your Fragment2 class, you call your action and pass your argument:
val action = Fragment2Directions.action2To1("MY_STRING_ARGUMENT")
findNavController().navigate(action)