TextField breaks composing when used with Jetpack navigation?

Viewed 322

I’ve been trying to put a simple app together, using Compose 1.0.0-beta09, Kotlin 1.5.10, and Jetpack Navigation 2.3.4. The app has one activity and two fragments.

First (main) fragment /screen (clicking in the button takes me to the second fragment/screen ): Screen One screenshot

Second fragment:/screen: Screen Two screenshot

Problem: After interacting with (putting the cursor in) the TextField on the first screen and subsequently clicking on the button, the second screen loads but is empty (the onCreateView of the SecondFragment is called but the setContent doesn’t work / the screen doesn’t get recomposed?). If I don’t interact with the TextField, the problem doesn’t happen. I’ve tested on emulators with API levels 28 & 30, compose 1.0.0-beta0709, Kotlin 1.4.32 & 1.5.10 with similar results.

Empty Screen Two

Main classes:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AndroidViewBinding(ContentMainBinding::inflate)
        }
    }
}
class FirstFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = ComposeView(inflater.context).apply {
        setContent {
            FirstScreen( onButtonClick = {
                findNavController().navigate(R.id.nav_second_fragment)
            })
        }
    }
}
@Composable
fun FirstScreen(onButtonClick: () -> Unit) {
    Column {
        Text("Screen One", color = Color.Blue, fontSize = 30.sp)
        Button(
            onClick = {
                onButtonClick() },
            content = {
                Text(text = "go to Screen Two", color = Color.White)
            })
        TextField(
            value = "",
            onValueChange = {},
            label = { Text(stringResource(R.string.label_enter_value)) },
        )
    }
}
class SecondFragment : Fragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View = ComposeView(inflater.context).apply {

         setContent {
            Column {
                Text("Screen Two", color = Color.Blue, fontSize = 30.sp)
            }
        }
    }
}
plugins {
    id 'com.android.application'
    id 'kotlin-android'
}

android {
    compileSdk 30

    defaultConfig {
        applicationId "com.example.composewithnavigation"
        minSdk 28
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary true
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
        useIR = true
    }
    buildFeatures {
        compose true
        viewBinding true
    }
    composeOptions {
        kotlinCompilerExtensionVersion compose_version
        kotlinCompilerVersion '1.5.10'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.5.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling:$compose_version"
    implementation "androidx.compose.ui:ui-viewbinding:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.3.0-beta01'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.4'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.4'

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
}
1 Answers

As far as I know, fragments are discouraged in Compose for navigation. You are supposed to be creating multiple screens, like the FirstScreen Composable here. Anyway, I think the reason for the setContent not being called is that it is called from within the other setContent from your first fragment. Inside the setContent, you make the navigation call, which redirects it to the second fragment, which calls a setContent yet again. Because the second fragment will already be displayed inside the setContent of the FirstScreen Composable, your are actually nesting it, which might not be supported by compose , (I mean, makes sense).

That is why we recommend ditching the fragments and using Composables instead, which do not require explicitly calling setContent

Related