Receive result from DialogFragment
Asked Answered
H

17

255

I am using DialogFragments for a number of things: choosing item from list, entering text.

What is the best way to return a value (i.e. a string or an item from a list) back to the calling activity/fragment?

Currently I am making the calling activity implement DismissListener and giving the DialogFragment a reference to the activity. The Dialog then calls the OnDimiss method in the activity and the activity grabs the result from the DialogFragment object. Very messy and it doesn't work on configuration change (orientation change) as the DialogFragment loses the reference to the activity.

Thanks for any help.

Horde answered 5/6, 2012 at 21:35 Comment(4)
DialogFragments are still just fragments. Your approach is actually the recommended way for fragments to use to talk back to the main activity. developer.android.com/guide/topics/fundamentals/…Brindisi
Thanks for that. I was very close (as you said). The bit that that linked document helped me with was using onAttach() and casting the activity to a listener.Horde
@codinguser, @Styx - "giving the DialogFragment a reference to the activity" - this detail is a little risky, as both the Activity and the DialogFragment might be recreated. Using the Activity passed to onAttach(Activity activity) is the proper and recommended way.Scratch
check my answer here #55946719Refectory
Z
261

Use myDialogFragment.setTargetFragment(this, MY_REQUEST_CODE) from the place where you show the dialog, and then when your dialog is finished, from it you can call getTargetFragment().onActivityResult(getTargetRequestCode(), ...), and implement onActivityResult() in the containing fragment.

It seems like an abuse of onActivityResult(), especially as it doesn't involve activities at all. But I've seen it recommended by official google people, and maybe even in the api demos. I think it's what g/setTargetFragment() were added for.

Zerlina answered 17/11, 2012 at 19:1 Comment(11)
setTargetFragment mentions that the requestcode is for use in onActivityResult so I guess it's ok to use this approach.Osman
What if the target is an activity?Benkley
If target is activity I would declare interface with method like "void onActivityResult2(int requestCode, int resultCode, Intent data)" and implement it by an Activity. In DialogFragment just getActivity and check for this interface and call it appropriately.Boggers
What about if we want to access the DialogFragment views (for example, change the text of a TextView) in onActivityResult?Mcneill
Interface is way to goPhare
but how can we do this job if I call an implicit Intent (like call camera application) from DialogFragment, ?Kristelkristen
Make sure you use getFragmentManager() and not getChildFragmentManager() when you add the DialogFragment. The latter will throw Fragement no longer exists for key android:target_state: if you call setTargetFragment() with a non-sibling fragment. See this issue for details. Alternatively, on API 17+, use getParentFragment() from the DialogFragment.Studley
but how to receive call from dialog fragment in a dialog fragmentCarpus
It is not good solution. It will not working after save and restore dialog fragment state. LocalBroadcastManager is best solution in this case.Reddick
@Reddick That's just not true. It's the best solution. There is no problem when saving and restoring the state. If you ever had a problem you used the wrong fragment manager. The target fragment / caller hast to use getChildFragmentManager() to show the dialog.Springlet
setTargetFragment is now deprecated, but the replacement FragmentManager.setFragmentResultListener (described in Pass data between fragments is still in alpha.Rangel
C
143

As you can see here there is a very simple way to do that.

In your DialogFragment add an interface listener like:

public interface EditNameDialogListener {
    void onFinishEditDialog(String inputText);
}

Then, add a reference to that listener:

private EditNameDialogListener listener;

This will be used to "activate" the listener method(s), and also to check if the parent Activity/Fragment implements this interface (see below).

In the Activity/FragmentActivity/Fragment that "called" the DialogFragment simply implement this interface.

In your DialogFragment all you need to add at the point where you'd like to dismiss the DialogFragment and return the result is this:

listener.onFinishEditDialog(mEditText.getText().toString());
this.dismiss();

Where mEditText.getText().toString() is what will be passed back to the calling Activity.

Note that if you want to return something else simply change the arguments the listener takes.

Finally, you should check whether the interface was actually implemented by the parent activity/fragment:

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Verify that the host activity implements the callback interface
    try {
        // Instantiate the EditNameDialogListener so we can send events to the host
        listener = (EditNameDialogListener) context;
    } catch (ClassCastException e) {
        // The activity doesn't implement the interface, throw exception
        throw new ClassCastException(context.toString()
                + " must implement EditNameDialogListener");
    }
}

This technique is very flexible and allow calling back with the result even if your don;t want to dismiss the dialog just yet.

Cajuput answered 11/2, 2013 at 8:34 Comment(14)
This works great with Activity's and FragmentActivity's but if is the caller a Fragment?Raynor
I'm not sure I fully understand you. But it will work the same if the caller is a Fragment.Cajuput
But how do you get the instance of the Fragment? There is not a getFragment() function.Raynor
If the caller was a Fragment then you can do a few things: 1. Pass the fragment as a reference (Might not be a good idea because you might cause memory leaks). 2. Use the FragmentManager and call findFragmentById or findFragmentByTag it will get the fragments that exist in your activity. I hope it helped. Have a great day!Cajuput
The problem with this approach is that fragment are not very good at retaining object since they are meant to be recreated, for instance try to change the orientation, the OS will recreate the fragment but the instance of the listener will not be available anymoreDangelo
@BraisGabin hi got any solution for geting the instance of the Fragment?Pasquil
@Pasquil look at the @Timmmm's answer. setTargetFragment() and getTargetFragment() are magic.Raynor
I recommend protecting from ClassCastException by checking if the activity indeed implements the interface. There might be an activity that doesn't care about the result, for example, always refreshing the data in the background using a DB listener.Selfdrive
Yes, this event callback is exactly how the Android docs suggest doing this. Should be the accepted answer IMHO. developer.android.com/guide/topics/ui/dialogs.htmlUnmentionable
You also need to null out listener in onDetach() or you will leak your hosting fragment/activity big time!!!Stogy
Check with instanceof instead of try/catching. Also, if you're throwing a new exception, add the previous exception in the constructor...Triny
This answer is the same answer that Android gives here: developer.android.com/guide/topics/ui/…. Also, I've used this same strategy (with the Support Library versions), switched orientation while dialog is up, and the callbacks still work. In fact, I have yet to personally discover a situation in which this answer (which is also the documented answer) fails to work if executed as described. What am I missing?Warmth
A disadvantage of this solution is that the host (activity or fragment) itself must implement the listener interface. I would like to instead use a anonymous listener class, but I can come up with a nice way to do that.Lieselotteliestal
and if you have more than 1 dialog by fragment or activity? This doesn't workFiore
K
52

There is a much simpler way to receive a result from a DialogFragment.

First, in your Activity, Fragment, or FragmentActivity you need to add in the following information:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Stuff to do, dependent on requestCode and resultCode
    if(requestCode == 1) { // 1 is an arbitrary number, can be any int
         // This is the return result of your DialogFragment
         if(resultCode == 1) { // 1 is an arbitrary number, can be any int
              // Now do what you need to do after the dialog dismisses.
         }
     }
}

The requestCode is basically your int label for the DialogFragment you called, I'll show how this works in a second. The resultCode is the code that you send back from the DialogFragment telling your current waiting Activity, Fragment, or FragmentActivity what happened.

The next piece of code to go in is the call to the DialogFragment. An example is here:

DialogFragment dialogFrag = new MyDialogFragment();
// This is the requestCode that you are sending.
dialogFrag.setTargetFragment(this, 1);     
// This is the tag, "dialog" being sent.
dialogFrag.show(getFragmentManager(), "dialog");

With these three lines you are declaring your DialogFragment, setting a requestCode (which will call the onActivityResult(...) once the Dialog is dismissed, and you are then showing the dialog. It's that simple.

Now, in your DialogFragment you need to just add one line directly before the dismiss() so that you send a resultCode back to the onActivityResult().

getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, getActivity().getIntent());
dismiss();

That's it. Note, the resultCode is defined as int resultCode which I've set to resultCode = 1; in this case.

That's it, you can now send the result of your DialogFragment back to your calling Activity, Fragment, or FragmentActivity.

Also, it looks like this information was posted previously, but there wasn't a sufficient example given so I thought I'd provide more detail.

EDIT 06.24.2016 I apologize for the misleading code above. But you most certainly cannot receive the result back to the activity seeing as the line:

dialogFrag.setTargetFragment(this, 1);

sets a target Fragment and not Activity. So in order to do this you need to use implement an InterfaceCommunicator.

In your DialogFragment set a global variable

public InterfaceCommunicator interfaceCommunicator;

Create a public function to handle it

public interface InterfaceCommunicator {
    void sendRequestCode(int code);
}

Then when you're ready to send the code back to the Activity when the DialogFragment is done running, you simply add the line before you dismiss(); your DialogFragment:

interfaceCommunicator.sendRequestCode(1); // the parameter is any int code you choose.

In your activity now you have to do two things, the first is to remove that one line of code that is no longer applicable:

dialogFrag.setTargetFragment(this, 1);  

Then implement the interface and you're all done. You can do that by adding the following line to the implements clause at the very top of your class:

public class MyClass Activity implements MyDialogFragment.InterfaceCommunicator

And then @Override the function in the activity,

@Override
public void sendRequestCode(int code) {
    // your code here
}

You use this interface method just like you would the onActivityResult() method. Except the interface method is for DialogFragments and the other is for Fragments.

Kolo answered 15/8, 2014 at 13:58 Comment(12)
This approach will not work if target is Activity because you can't call its onActivityResult (from your DialogFragment) due to protected access level.Boggers
That is simply not true. I use this exact code in my projects. That is where I pulled it from and it works just fine. Please remember if you are having this protected access level issue you can change your access level for any method and class from protected to private or public if necessary.Kolo
If you change your Activity's onActivityResult() to public then you'll have to cast to that Activity when referencing it in the DialogFragment to access that method, in which case you may as well have used Assaf's solution. Or am I missing something here?Shebashebang
Hi, you say you can call dialogFrag.setTargetFragment(this, 1) from an Activity, but this method receives a Fragment as first argument, so this couldn't be casted. Am I right ?Multiracial
You are indeed right Mondkin. See here: developer.android.com/reference/android/app/…Cop
Im sorry, but how to attach data such as String or int?Atronna
but how to receive call from dialog fragment in a dialog fragmentCarpus
I absolutely do not see how this works since, as @Mondkin says, you can't call dialogFrag.setTargetFragment(this, 1) from an Activity.Stogy
I'll post some responses for you all in a few to explain the activity stuff.Kolo
java.lang.NullPointerException: Attempt to invoke interface method 'void androidessence.comman.EditSessionLengthDialog$InterfaceCommunicator.sendRequestCode(int)' on a null object referenceDefendant
@Kolo I am getting a NullPointer on InterfaceCommunicator.sendRequestCode(int). Any advice?Vocalism
@Defendant @Vocalism you probably need to override onAttach(Context context) in your DialogFragment. Like so: @Override public void onAttach(Context context) { super.onAttach(context); yourInterface = (YourInterface) context; }Vellicate
T
26

For anyone still reading this: setTargetFragment() has been deprecated. It is now recommended to use the FragmentResultListener API like this:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setFragmentResultListener("requestKey") { key, bundle ->
        val result = bundle.getString("resultKey")
        // Do something with the result...
    }

    ...

    // Somewhere show your dialog
    MyDialogFragment.newInstance().show(parentFragmentManager, "tag")
}

Then in your MyDialogFragment set the result:

button.setOnClickListener{
    val result = "some string"
    setFragmentResult("requestKey", bundleOf("resultKey" to result))
    dismiss()
}
Takeover answered 5/8, 2020 at 9:57 Comment(4)
With the release of Fragment library 1.3.0 (developer.android.com/jetpack/androidx/releases/…) this will be the "most correct" answer. Currently it is only available through alpha releases which should not be used in production.Oshaughnessy
parentFragmentManager is important. It's easy to send childFragmentManager by accident which doesn't trigger setFragmentResultListener lambdaPascale
How to do the Result.ACTIVITY_CANCEL with a dialog fragment in this new way?Race
You can use the extension available in fragment-ktx: setFragmentResultListenerAmateurish
L
22

Well its too late may be to answer but here is what i did to get results back from the DialogFragment. very similar to @brandon's answer. Here i am calling DialogFragment from a fragment, just place this code where you are calling your dialog.

FragmentManager fragmentManager = getFragmentManager();
            categoryDialog.setTargetFragment(this,1);
            categoryDialog.show(fragmentManager, "dialog");

where categoryDialog is my DialogFragment which i want to call and after this in your implementation of dialogfragment place this code where you are setting your data in intent. The value of resultCode is 1 you can set it or use system Defined.

            Intent intent = new Intent();
            intent.putExtra("listdata", stringData);
            getTargetFragment().onActivityResult(getTargetRequestCode(), resultCode, intent);
            getDialog().dismiss();

now its time to get back to to the calling fragment and implement this method. check for data validity or result success if you want with resultCode and requestCode in if condition.

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);        
        //do what ever you want here, and get the result from intent like below
        String myData = data.getStringExtra("listdata");
Toast.makeText(getActivity(),data.getStringExtra("listdata"),Toast.LENGTH_SHORT).show();
    }
Lynsey answered 9/4, 2016 at 8:25 Comment(0)
V
11

Different approach, to allow a Fragment to communicate up to its Activity:

1) Define a public interface in the fragment and create a variable for it

public OnFragmentInteractionListener mCallback;

public interface OnFragmentInteractionListener {
    void onFragmentInteraction(int id);
}

2) Cast the activity to the mCallback variable in the fragment

try {
    mCallback = (OnFragmentInteractionListener) getActivity();
} catch (Exception e) {
    Log.d(TAG, e.getMessage());
}

3) Implement the listener in your activity

public class MainActivity extends AppCompatActivity implements DFragment.OnFragmentInteractionListener  {
     //your code here
}

4) Override the OnFragmentInteraction in the activity

@Override
public void onFragmentInteraction(int id) {
    Log.d(TAG, "received from fragment: " + id);
}

More info on it: https://developer.android.com/training/basics/fragments/communicating.html

Vocalism answered 30/7, 2017 at 19:0 Comment(1)
Thanks for summarizing it so well. Just one note for others, the Android Devs tutorial suggests to override the public void onAttach of the fragment and do the activity casting thereUnread
W
10

One easy way I found was the following: Implement this is your dialogFragment,

  CallingActivity callingActivity = (CallingActivity) getActivity();
  callingActivity.onUserSelectValue("insert selected value here");
  dismiss();

And then in the activity that called the Dialog Fragment create the appropriate function as such:

 public void onUserSelectValue(String selectedValue) {

        // TODO add your implementation.
      Toast.makeText(getBaseContext(), ""+ selectedValue, Toast.LENGTH_LONG).show();
    }

The Toast is to show that it works. Worked for me.

Whosoever answered 23/12, 2015 at 22:44 Comment(2)
I am not sure if it is a right way to do it, but it certainly works :)Asclepiadaceous
Better use Interface rather than hard-coupling with concrete classes.Cyrille
C
7

I'm very surprised to see that no-one has suggested using local broadcasts for DialogFragment to Activity communication! I find it to be so much simpler and cleaner than other suggestions. Essentially, you register for your Activity to listen out for the broadcasts and you send the local broadcasts from your DialogFragment instances. Simple. For a step-by-step guide on how to set it all up, see here.

Cop answered 4/10, 2015 at 18:20 Comment(8)
I like that solution, is this considered to be a good or best practice in Android?Unmannered
I really liked the tutorial, thank you for posting it. I do want to add that depending on what you are trying to accomplish either method may be more useful than the other. I would suggest the local broadcast route if you have multiple input/results being sent back to the activity from the dialog. I would recommend using the onActivityResult route if your output is very basic/simple. So, to answer the best practice question, it depends on what you are trying to accomplish!Kolo
Benjamin, best practice... I think it's pretty clear from the number of people unclear on how to pass data from a DialogFragment to an Activity that there is a lack of best practice here :-/ I used to use the interface approach in the past but I try to avoid type-checking and explicit casting of classes these days.Cop
Brandon, see Mondkin's comment in your answer. Your approach might work when starting a Fragment from a Fragment but not when starting a Fragment from an Activity.Cop
@AdilHussain You're right. I made the assumption that people were using Fragments within their Activities. The setTargetFragment option is great if you're communicating with a Fragment and a DialogFragment. But you need to use the Broadcast method when it's an Activity calling the DialogFragment.Kolo
For the love of Foo, do not use Broadcasts!! It opens your application to a slue of security issues. Also I find that the worst android application I have to work on abuse broadcasts. Can you think of a better way to make code completely unusable? Now I am having to root out broadcast receivers, instead of a CLEAR line of code? To be clear there are uses for broadcoasts, but not in this context! NEVER in this context! Its just sloppy. Local or not. Callbacks are all you need.Varner
I like the concept of using a publish/subscribe approach. I'm using Guava EventBus github.com/google/guava/wiki/EventBusExplained, a very lightweight component. I instantiate an event bus and make it available to the dialog. When the dialog is ready it simply posts an event and my caller receives the event. All happens internally with no security issues and as importantly, very fast.Gwennie
As well as the Guava EventBus another option is the GreenRobot EventBus. I've not used the Guava EventBus but have used the GreenRobot EventBus and have had a good experience with it. Nice and simple to use. For a small example of how to architecture an Android application to use the GreenRobot EventBus, see here.Cop
S
4

Or share ViewModel like showed here:

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;
    }
}


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);
        });
    }
}

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.
        });
    }
}

https://developer.android.com/topic/libraries/architecture/viewmodel#sharing_data_between_fragments

Sunbeam answered 29/10, 2018 at 15:0 Comment(0)
I
3

In my case I needed to pass arguments to a targetFragment. But I got exception "Fragment already active". So I declared an Interface in my DialogFragment which parentFragment implemented. When parentFragment started a DialogFragment , it set itself as TargetFragment. Then in DialogFragment I called

 ((Interface)getTargetFragment()).onSomething(selectedListPosition);
Idolla answered 3/7, 2017 at 8:51 Comment(0)
R
3

In Kotlin

    // My DialogFragment
class FiltroDialogFragment : DialogFragment(), View.OnClickListener {
    
    var listener: InterfaceCommunicator? = null

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        listener = context as InterfaceCommunicator
    }

    interface InterfaceCommunicator {
        fun sendRequest(value: String)
    }   

    override fun onClick(v: View) {
        when (v.id) {
            R.id.buttonOk -> {    
        //You can change value             
                listener?.sendRequest('send data')
                dismiss()
            }
            
        }
    }
}

// My Activity

class MyActivity: AppCompatActivity(),FiltroDialogFragment.InterfaceCommunicator {

    override fun sendRequest(value: String) {
    // :)
    Toast.makeText(this, value, Toast.LENGTH_LONG).show()
    }
}
 

I hope it serves, if you can improve please edit it. My English is not very good

Rashid answered 13/10, 2018 at 22:24 Comment(1)
In my case the dialog is created from a fragment not activity so this solution does not work. But I like the smiley you put :)Ennoble
P
1

if you want to send arguments and receive the result from second fragment, you may use Fragment.setArguments to accomplish this task

static class FirstFragment extends Fragment {
    final Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case 101: // receive the result from SecondFragment
                Object result = msg.obj;
                // do something according to the result
                break;
            }
        };
    };

    void onStartSecondFragments() {
        Message msg = Message.obtain(mUIHandler, 101, 102, 103, new Object()); // replace Object with a Parcelable if you want to across Save/Restore
                                                                               // instance
        putParcelable(new SecondFragment(), msg).show(getFragmentManager().beginTransaction(), null);
    }
}

static class SecondFragment extends DialogFragment {
    Message mMsg; // arguments from the caller/FirstFragment

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onViewCreated(view, savedInstanceState);
        mMsg = getParcelable(this);
    }

    void onClickOK() {
        mMsg.obj = new Object(); // send the result to the caller/FirstFragment
        mMsg.sendToTarget();
    }
}

static <T extends Fragment> T putParcelable(T f, Parcelable arg) {
    if (f.getArguments() == null) {
        f.setArguments(new Bundle());
    }
    f.getArguments().putParcelable("extra_args", arg);
    return f;
}
static <T extends Parcelable> T getParcelable(Fragment f) {
    return f.getArguments().getParcelable("extra_args");
}
Predella answered 26/2, 2019 at 2:25 Comment(0)
W
1

Various Approaches (2023):

  • Using Navigation graph: Just set argument for return direction (from dialog fragment to its parent) and receive it in destination.

Send from Dialog (java): NavHostFragment.navigate(this, YourDialogName+Directions .action+---+To---(DefinedArgument)));

Receive in parent (java): dialogResult = NameOfFragmentArgs.fromBundle(requireArguments()).get+DefinedArgument;

  • using Navigation graph and ViewModel+LiveData (recomended)

for this approach there is a complete example in CommonsWare website

  • Using Intent and Bundle

  • Using custom UI for dialog then you have direct access to setOnClickListener method for positive and negative buttons inside of fragment;

  • Implementing your defined interface e.g. setOnYes in destination fragment/activity

  • Using FragmentManager and setFragmentResult(String, Bundle) See this post

Warfeld answered 16/3, 2023 at 15:59 Comment(0)
K
0

TL;DR - use this AppDialog class to both pass data into DialogFragment as well as get result out of it.

Detailed explanation:

  • Premise - Fragments get destroyed and recreated on config changes. View models hang around. When using a Dialog, it is recommended to wrap it in DialogFragment so that when the user rotates device and changes orientation the Dialog will not unexpectedly disappear (the DialogFragment will re-create it and re-display it).
  • Limitation (hence this question) - The way the DialogFragment works is it takes a class that it will need to re-instantiate on configuration changes - that means one can't have constructor parameters to the subclass to pass parameters, and typically one needs to make custom callbacks through a view model to pass back result of dialog. That typically means a new subclass for every dialog.
  • The solution - To help with all this, this custom AppDialog fragment comes to the rescue - the parameters are stored in-memory (similar to view model, you can think of it as a tiny custom view model that holds T in memory and uses it to re-create the dialog on config changes) until the dialog fragment is dismissed. The proper way to call back would be through a view model. If the fragment that shows the AppDialog, then you probably already have a view model and you can reference it from the lambda used to create the dialog - that means additional strong reference to the view model until the dialog fragment is dismissed.
  • Example - see the examples where a simple Dialog is refactored to use this AppDialog utility class to both receive a parameter and do a callback to viewModel to notify of result.

The helper class:


class AppDialog<T>: DialogFragment() {
    companion object {

        fun<T> buildDialog(params: T? = null, builder: AppDialogLambda<T>): AppDialog<T> {

            // Setup arguments
            val args = Bundle()
            args.putInt("key", pushDialogArgs(params, builder))

            // Instantiate
            val fragment = AppDialog<T>()
            fragment.arguments = args
            return fragment
        }

        // --------------------
        // Dialog Arguments

        private var lastKey: Int = 0
        private val dialogArgs = mutableMapOf<Int, Pair<Any?, AppDialogLambda<*>>>()

        private fun pushDialogArgs(params: Any?, builder: AppDialogLambda<*>): Int {
            dialogArgs[lastKey] = params to builder
            return lastKey++
        }

        private fun getDialogArgs(key: Int): Pair<Any?, AppDialogLambda<*>> {
            return dialogArgs[key]!!
        }

        private fun deleteDialogArgs(key: Int) {
            dialogArgs.remove(key)
        }
    }

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        // Get arguments
        val argKey = requireArguments().getInt("key")
        val (params, builder) = getDialogArgs(argKey)

        // We are getting back our arguments we passed AppDialog.buildDialog and
        // the type is guaranteed to be the same. Silence this warning
        @Suppress("UNCHECKED_CAST")
        return (builder as AppDialogLambda<T>)(this, params as T?)
    }

    override fun onDismiss(dialog: DialogInterface) {
        super.onDismiss(dialog)
        val argKey = requireArguments().getInt("key")
        deleteDialogArgs(argKey)
    }
}

Example usage (after):

        val info = mapOf("message" to "${error.description}\n\nPlease check your Internet connection and try again.")
        AppDialog.buildDialog(info) { fragment, params ->
            fragment.isCancelable = false // since we are in a DialogFragment
            AlertDialog.Builder(fragment.context)
                .setTitle("Terms Of Service Failed To Load")
                .setMessage(params!!["message"])
                .setPositiveButton("Retry") { _, _ ->
                    // Update the view model instead of calling UserTOSFragment directly 
                    // as the fragment may be destroyed and recreated
                    // on configuration changes. The viewModel will stay alive.
                    viewModel.onTermsOfServiceReload()
                }
                .setNegativeButton("Cancel") { _, _ ->
                    viewModel.onTermsOfServiceDeclined()
                    fragment.findNavController().popBackStack()
                }.create()
        }.show(parentFragmentManager, "TOS Failed Dialog")

Example usage (before): Without using DialogFragment (for illustration purposes, don't do this, this is bad practice as the dialog will be destroyed on config changes), code inside UserTOSFragment.kt - note code used to call directly the UserTOSFragment.loadContent() on retry. This has to be rewritten to instead call viewModel.onTermsOfServiceDeclined() in the above example:

        AlertDialog.Builder(context)
            .setTitle("Terms Of Service Failed To Load")
            .setMessage("${error.description}\n\nPlease check your Internet connection and try again.")
            .setPositiveButton("Retry") { _, _ ->
                loadContent()
            }
            .setCancelable(false)
            .setNegativeButton("Cancel") { _, _ ->
                viewModel.onTermsOfServiceDeclined()
                findNavController().popBackStack()
            }
            .show()
Koss answered 13/11, 2021 at 21:6 Comment(0)
E
0

The marked answer is outdated. Use the new Fragment result API

but instead of:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
    setFragmentResultListener("requestKey") { requestKey, bundle ->
        val result = bundle.getString("bundleKey")
    }
}

which work for activities and standard root fragment-to-root fragment communication, use:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    
 childFragmentManager.setFragmentResultListener("requestKey",this/*lifecycleOwner*/) { requestKey, bundle ->
        val result = bundle.getString("bundleKey")
    }
}

and show the dialogFragment like:

MyDialogFragment().show(childFragmentManager, "tag")
Electrostatics answered 25/7, 2023 at 14:4 Comment(0)
A
-2

On a dialog Fragment

class AbcDialogFragment(private val ondata: (data: String) -> Unit) :  DialogFragment() {}

Code to show the dialog from fragment/Activity

val abcDialogFragment = AbcDialogFragment(ondata = {data->  })
                
abcDialogFragment.show(requireActivity().supportFragmentManager, "TAG")

and in the dialog fragment, you can invoke the onData when dialog fragment is closed or any click listeners.

Angular answered 15/4, 2021 at 3:4 Comment(0)
W
-3

Just to have it as one of the options (since no one mentioned it yet) - you could use an event bus like Otto. So in the dialog you do:

bus.post(new AnswerAvailableEvent(42));

And have your caller (Activity or Fragment) subscribe to it:

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
   // TODO: React to the event somehow!
}
Whereto answered 20/9, 2017 at 16:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.