Search code examples
androidandroid-fragmentsandroid-alertdialogandroid-fragmentactivityandroid-dialogfragment

How do I safely re-create my activity when I switch between dark and light mode when using Fragment dialog?


I have a dialog fragment that displays an image whose bytecode is passed to the constructor.

When the dialog is appeared and I am in dark mode and try to change the mode to light one and I come back to my screen, the screen appears all white and I get the error below :

 Caused by: androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment *.*.*.PictureLoaderDialogFragment: could not find Fragment constructor

I instantiate my fragment dialog as below:

class PictureLoaderDialogFragment(
    private var bitmapByteArray: ByteArray?
) : DialogFragment(){

   companion object {

        @JvmStatic
        fun newInstance(
            bitmapByteArray: ByteArray? = null
        ) = PictureLoaderDialogFragment(bitmapByteArray)
    }
}

Any idea how it should be fixed ?


Solution

  • This error is caused by passing an argument to your fragment's constructor.

    This has to do with the life cycle of fragments and their interaction with the activity. Fragments in Android are designed to be reusable and can be created and destroyed at different points in time depending on the activity's lifecycle. When creating fragments, the Android system uses an empty constructor with no arguments to create a new instance of the fragment after the device configuration changes (in this case, the device theme changes).

    In order to solve this problem, there are 3 solutions:

    1. Save arguments in a Bundle
    2. Use FragmentFactory
    3. Use Dependency Injection (DI)

    Since you used the newInstance method in your code, the following example shows how to use it, following the first way and saving arguments in a Bundle:

    class PictureLoaderDialogFragment : DialogFragment() {
    
        //...
    
        companion object {
            fun newInstance(bitmapByteArray: ByteArray? = null): PictureLoaderDialogFragment {
                val fragment = PictureLoaderDialogFragment()
                val args = Bundle()
                args.putByteArray("bitmap", bitmapByteArray)
                fragment.arguments = args
                return fragment
            }
        }
    }
    

    You can access the passed arguments inside the PictureLoaderDialogFragment:

    val bitmapByteArray = arguments?.getByteArray("bitmap")
    bitmapByteArray?.let {
        val image = BitmapFactory.decodeByteArray(it, 0, it.size)
        imageView.setImageBitmap(image)
    }
    

    You can show your DialogFragment:

    PictureLoaderDialogFragment.newInstance(bitmapByteArray).show(manager, tag)
    

    This article explains how you can use FragmentFactory by following the second solution way

    The third way to solvehere, you can learn how to use dependency injection using Hilt