问题
I have a workflow with 3 screens. From "screen 1" to access to "screen 2", the user must accept some kind of terms and conditions that I call in my picture "modal". But he only has to accept those conditions once. The next time he is on the first screen, he can go directly to screen 2. The user can chose to NOT accept the terms, and therefore we go back to "screen 1" and do not try to go to "screen 2".
I am wondering how to do it with the new navigation component.
Previously, what I would do it:
- On screen 1, check if the user must accept the conditions
- If no, start "screen 2" activity
- If yes, use
startActivityForResult()
and wait result from the modal. Mark the terms as accepted. Start "screen 2"
But with the navigation graph, there is no way to start a Fragment to obtain a result.
I could mark the terms as accepted in the "modal" screen and start the "screen 2" from there. The thing is that to access to the screen 2, I need to do a network request. I do not want to duplicate the call to the API and processing its outcome in both "screen 1" and "modal".
Is there a way to go back from "modal" to "screen 1" with some information (user accepted the terms), using Jetpack navigation?
Edit: I currently get around it by using the same flow that Yahya suggests below: using an Activity just for the modal and using startActivityForResult
from the "screen 1". I am just wondering if I could continue to use the navigation graph for the whole flow.
回答1:
Recently (in androidx-navigation-2.3.0-alpha02 ) Google was released a correct way for achieve this behaviour with fragments.
In short: (from release note)
If Fragment A
needs a result from Fragment B
..
A
should get the savedStateHandle from the currentBackStackEntry, call getLiveData providing a key and observe the result.
findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Type>("key")?.observe(
viewLifecycleOwner) {result ->
// Do something with the result.
}
B
should get the savedStateHandle from the previousBackStackEntry, and set the result with the same key as the LiveData in A
findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)
Related documentation
回答2:
There are a couple of alternatives to the shared view model.
fun navigateBackWithResult(result: Bundle) as explained here https://medium.com/google-developer-experts/using-navigation-architecture-component-in-a-large-banking-app-ac84936a42c2
Create a callback.
ResultCallback.kt
interface ResultCallback : Serializable {
fun setResult(result: Result)
}
Pass this callback as an argument (note it has to implement Serializable and the interface needs to be declared in its own file.)
<argument android:name="callback"
app:argType="com.yourpackage.to.ResultCallback"/>
Make framgent A implement ResultCallback, fragment B by will get the arguments and pass the data back through them, args.callback.setResult(x)
回答3:
It looks like there isn't equivalent for startActivityForResult
in Navigation Component right now. But if you're using LiveData and ViewModel you may be interested in this article. Author is using activity scoped ViewModel and LiveData to achieve this for fragments.
回答4:
There is another alternative workaround. You can use another navigation action from your modal back to screen1, instead of using popBackStack()
. On that action you can send whatever data you like to screen one. Use this strategy to make sure the modal screen isn't then kept in the navigation back stack: https://stackoverflow.com/a/54015319/4672107.
The only issue I see with this strategy is that pressing the back button will not send any data back, however most use cases require navigation after a specific user action and and in those situations, this workaround will work.
回答5:
To get the caller fragment, use something like fragmentManager.putFragment(args, TargetFragment.EXTRA_CALLER, this)
, and then in the target fragment get the caller fragment using
if (args.containsKey(EXTRA_CALLER)) {
caller = fragmentManager?.getFragment(args, EXTRA_CALLER)
if (caller != null) {
if (caller is ResultCallback) {
this.callback = caller
}
}
}
来源:https://stackoverflow.com/questions/50702643/equivalent-of-startactivityforresult-with-android-architecture-navigation