How to show/hide BottomSheetDialogFragment without recreating it?

2k Views Asked by At

I am using BottomSheetDialogFragment in my android app. I am using Java. I show the bottomsheet by:

 ActionBottomDialogFragment dialogFragment = ActionBottomDialogFragment.newInstance();

        showButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dialogFragment.show(getActivity().getSupportFragmentManager(), ActionBottomDialogFragment.TAG);
            }
        });

What I see is it calls onCreateDialog method and then calls onViewCreated methods. For the first time this is okay.

Now I hide the bottom sheet using:

ImageButton close = view.findViewById(R.id.closeButton);
        close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

Then when I press the show button again, it calls onCreateDialog method again. I have a dynamic list of choice chips which I want the state to be just as I left it. If I left it on checking 'Choice A', it should show up selected the next time I open the bottom sheet. I need the state to be maintained.

What is happening is it rebuilds the choice chips from start, so the state is lost.

How can I just show / hide the bottom sheet without recreating?

2

There are 2 best solutions below

2
On

UI will be always created again when you call show(). It's better to use a sharedViewModel in bottomSheetFragment and the parent activity/fragment. You should save the fields being selected in the viewmodel and use it the bottomSheet. This way the state will be maintained.

0
On

You can use show() & hide() of the dialog itself; this will keep the fragment alive.

So, in your case:

To show it:

ActionBottomDialogFragment dialogFragment;

showButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (dialogFragment == null) {
             dialogFragment = ActionBottomDialogFragment.newInstance();
             dialogFragment.show(getActivity().getSupportFragmentManager(), ActionBottomDialogFragment.TAG);
        } else {
           dialogFragment.getDialog().show();
        }
    }
});

And to hide it:

ImageButton close = view.findViewById(R.id.closeButton);
        close.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
               dialogFragment.getDialog().hide();
            }
        });

But this requires to handle the side effects when dismissing the fragments using the back button and when touching outside.

Here's a custom DialogFragment that handles that:

public class BottomSheet extends BottomSheetDialogFragment {

    public BottomSheet() {
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.bottom_sheet_layout, container, false);
        return view;
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
        return new BottomSheetDialog(requireContext(), getTheme()) {
            @Override
            public void onBackPressed() {
                getDialog().hide();
            }
        };
]
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public void onStart() {
        super.onStart();
        
        setCancelable(false);
        getDialog().setCanceledOnTouchOutside(false);

        final View outsideView =
                requireDialog().findViewById(com.google.android.material.R.id.touch_outside);

        outsideView.setOnTouchListener((v, event) -> {
            if (event.getAction() == MotionEvent.ACTION_UP)
                getDialog().hide();
            return false;
        });


    }

}