How to get a result from fragment using Navigation Architecture Component?

后端 未结 6 459
无人共我
无人共我 2020-12-13 06:07

Let\'s say that we have two fragments: MainFragment and SelectionFragment. The second one is build for selecting some object, e.g. an integer. Ther

相关标签:
6条回答
  • 2020-12-13 06:32

    Since Fragment KTX 1.3.0-alpha04 Android supports passing data between fragments or between fragments and activities. It's similar to startActivityForResult logic.

    Here is an example with Navigation Component. You can read more about it here

    build.gradle

    implementation "androidx.fragment:fragment-ktx:1.3.0-alpha04"
    

    FragmentA.kt

    class FragmentA : Fragment() {
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
            // Step 1. Listen for fragment results
            setFragmentResultListener(FragmentB.REQUEST_KEY) { key, bundle -> 
                // read from the bundle
            }
    
            // Step 2. Navigate to Fragment B
            findNavController().navigate(R.id.fragmentB)
        }
    }
    

    FragmentB.kt

    class FragmentB : Fragment() {
    
        companion object {
            val REQUEST_KEY = "FragmentB_REQUEST_KEY"
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    
            buttonA.setOnClickListener { view -> 
                // Step 3. Set a result
                setFragmentResult(REQUEST_KEY, bundleOf("data" to "button clicked"))
    
                // Step 4. Go back to Fragment A
                findNavController().navigateUp()
            }
        }
    }    
    
    0 讨论(0)
  • 2020-12-13 06:35

    They have added a fix for this in the 2.3.0-alpha02 release.

    If navigating from Fragment A to Fragment B and A needs a result from B:

    findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<Type>("key")?.observe(viewLifecycleOwner) {result ->
        // Do something with the result.
    }
    

    If on Fragment B and need to set the result:

    findNavController().previousBackStackEntry?.savedStateHandle?.set("key", result)
    

    I ended up creating two extensions for this:

    fun Fragment.getNavigationResult(key: String = "result") =
        findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<String>(key)
    
    fun Fragment.setNavigationResult(result: String, key: String = "result") {
        findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
    }
    
    0 讨论(0)
  • 2020-12-13 06:37

    According to Google: you should try to use shared ViewModel. Check below example from Google:

    Shared ViewModel that will contain shared data and can be accessed from different fragments.

    public class SharedViewModel extends ViewModel {
        private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
    
        public void select(Item item) {
            selected.setValue(item);
        }
    
        public LiveData<Item> getSelected() {
            return selected;
        }
    }
    

    MasterFragment that updates ViewModel:

    public class MasterFragment extends Fragment {
    
        private SharedViewModel model;
    
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            itemSelector.setOnClickListener(item -> {
                model.select(item);
            });
        }
    }
    

    DetailsFragment that uses shared ViewModel:

    public class DetailFragment extends Fragment {
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
            model.getSelected().observe(this, item -> {
               // Update the UI.
            });
        }
    }
    
    0 讨论(0)
  • 2020-12-13 06:37

    Use these extension functions

    fun <T> Fragment.getNavigationResult(key: String = "result") =
        findNavController().currentBackStackEntry?.savedStateHandle?.get<T>(key)
    
    fun <T> Fragment.getNavigationResultLiveData(key: String = "result") =
            findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)
    
    fun <T> Fragment.setNavigationResult(result: T, key: String = "result") {
        findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
    }
    

    So if you want to send result from Fragment B to fragment A

    Inside Fragment B

    setNavigationResult(false, "someKey")
    

    Inside Fragment A

    val result = fragment.getNavigationResultLiveData<Boolean>("someKey")
    result.observe(viewLifecycleOwner){ booleanValue-> doSomething(booleanValue)
    
    0 讨论(0)
  • 2020-12-13 06:43

    with a little improvement of @LeHaine 's answer, you can use these methods for navigation 2.3.0-alpha02 and above

    fun <T> Fragment.getNavigationResult(key: String) =
        findNavController().currentBackStackEntry?.savedStateHandle?.getLiveData<T>(key)
    
    fun <T> Fragment.setNavigationResult(result: T, key: String) {
        findNavController().previousBackStackEntry?.savedStateHandle?.set(key, result)
    }
    
    0 讨论(0)
  • 2020-12-13 06:51

    I would suggest you to use NavigationResult library which is an add-on for JetPack's Navigation Component and lets you to navigateUp with Bundle. I've also wrote a blog post about this on Medium.

    0 讨论(0)
提交回复
热议问题