Incorrect work of the fragmentManager in conjunction with the Jetpack Compose

557 Views Asked by At

I added jetpack compose into my old project and I have noticed one problem with fragmentManager work.

In short, I have the following transactions:

MainActivity -> SettingsFragment -> BackgroundSettingsFragment.

When we go to BackgroundSettingsFragment and we press a back button, we return to SettingsFragment, but its content is empty. I debbuged SettingsFragment, after returning here, everything looks fine, but layout field is just empty. I added breakpoints to different lifecycle events (onCreate, onCreateView, onViewCreated). Everything works great, I see that resources also is fine.

Without jetpack compose dependencies, there is no problem.

In details, I have the following code.

    buildFeatures {
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
        kotlinCompilerVersion "$rootProject.kotlinVersion"
    }

    // Dependencies

    // --- Jetpack Compose ---
    implementation "androidx.compose.ui:ui:$compose_version"
    // Integration with activities
    implementation "androidx.activity:activity-compose:1.5.1"
    // Integration with ViewModels
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
    // Tooling support (Previews, etc.)
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    // Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
    implementation 'androidx.compose.foundation:foundation:1.2.0'
    // Material Design
    implementation "androidx.compose.material:material:$compose_version"
    // Material design icons
    implementation "androidx.compose.material:material-icons-core:$compose_version"
    implementation "androidx.compose.material:material-icons-extended:$compose_version"
    // Integration with observables
    implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
    implementation "androidx.compose.runtime:runtime-rxjava2:$compose_version"
    // Navigation
    implementation "androidx.navigation:navigation-compose:2.5.1"
    implementation "androidx.hilt:hilt-navigation-compose:1.0.0"


MainActivity:

class MainActivity : RxAppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(layout)
        
        redirectToSettingsFragment()
    }
    
    fun redirectToSettingsFragment() {
        replaceFragmentSafely(SettingsFragment(), R.id.contentFrame)
    }
}

SettingsFragment:

class SettingsFragment : RxFragment() {
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
        setupToolbar()
        setupView()
    }

    fun setupView() {
        label.setText(R.string.background_settings)
        value.setText(R.string.color)
    }
    
    fun setupToolbar() {
        (activity as MainActivity).toolbar.setOnBackButtonClickListener { 
            activity?.onBackPressed()
        }
    }
    
    fun redirectToSecondFragment() {
        replaceFragmentSafely(BackgroundSettingsFragment(), R.id.contentFrame, allowBackStack = true)
    }
}

BackgroundSettingsFragment:

class BackgroundSettingsFragment : RxFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
        setupToolbar()
    }
    
    fun setupToolbar() {
        (activity as MainActivity).toolbar.setOnBackButtonClickListener { 
            activity?.onBackPressed()
        }
    }
}
fun AppCompatActivity.replaceFragmentSafely(fragment: Fragment,
                                            @IdRes containerViewId: Int,
                                            allowBackStack: Boolean = false) {
    val fm = supportFragmentManager
            .beginTransaction()
            .replace(containerViewId, fragment, fragment.javaClass.toString())

    if (allowBackStack) fm.addToBackStack(null)

    try {
        if (!supportFragmentManager.isStateSaved) {
            fm.commit()
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

Here is the result. What SettingsFragment looks like before we go to the BackgroundSettingsFragment:

enter image description here

What SettingsFragment looks like after we returned from the BackgroundSettingsFragment:

enter image description here

Please help, maybe someone already faced such a problem.

1

There are 1 best solutions below

0
ph-pdy On

I had the same problem and was able to ... well, work around it :/

It seems that Kotlinx-synthetics (which you also seem to be using) and fragment back navigation do not work well with compose. As soon as I had migrated from Kotlinx-synthetics to view-binding, the bug was gone. See the official migration guide here: https://developer.android.com/topic/libraries/view-binding/migration

I hope it helps; cheers.