Handling multiple experimental annotations throughout an app

I have an app that makes heavy use of experimental features for Jetpack Compose so I have to declare a bunch of annotations on the composables. Since these annotations require callers to also declare them I have ended up in a situation where I have an activity with the following code:

import androidx.appcompat.app.AppCompatActivity

import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.ui.ExperimentalComposeUiApi

import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.permissions.ExperimentalPermissionsApi
…

class MainActivity : AppCompatActivity() {

    @ExperimentalPermissionsApi
    @ExperimentalComposeUiApi
    @ExperimentalPagerApi
    @ExperimentalMaterialNavigationApi
    @ExperimentalMaterialApi
    override fun onCreate(savedInstanceState: Bundle?) {
        // … wiring up compose code (which propagates the experimental annotations)

An alternative to avoid this situation would be to use the @OptIn instead but since only one is allowed per declaration it doesn't work out for my case with multiple experimental features.

Any way… This works fine — In Kotlin 1.5.

With Kotlin 1.6 I am getting a compilation error:

Opt-in requirement marker annotation on override requires the same marker on base declaration

But the base declaration is in the standard API that I cannot change. How can I make this compile (and work as before)?


I got tired of my code being polluted by all those annotations. The easiest way to get rid of them and have your code compile is just add this to your top build.gradle file - It's not exhaustive. Just add more compiler arguments for each annotation you need:

allprojects {
    tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
        kotlinOptions {
            freeCompilerArgs += [
                    "-Xuse-experimental=kotlin.ExperimentalUnsignedTypes",
                    "-Xuse-experimental=kotlinx.coroutines.ExperimentalCoroutinesApi",
                    "-Xuse-experimental=kotlinx.coroutines.InternalCoroutinesApi",
                    "-Xuse-experimental=androidx.compose.animation.ExperimentalAnimationApi",
                    "-Xuse-experimental=androidx.compose.ExperimentalComposeApi",
                    "-Xuse-experimental=androidx.compose.material.ExperimentalMaterialApi",
                    "-Xuse-experimental=androidx.compose.runtime.ExperimentalComposeApi",
                    "-Xuse-experimental=androidx.compose.ui.ExperimentalComposeUiApi",
                    "-Xuse-experimental=coil.annotation.ExperimentalCoilApi",
                    "-Xuse-experimental=kotlinx.serialization.ExperimentalSerializationApi",
                    "-Xuse-experimental=com.google.accompanist.pager.ExperimentalPagerApi"
            ]
        }
    }
}

A non-deprecated variation of @Johanns answer in Kotlin DSL (with some other annotations that I'm using):

Deprecation warning:

w: '-Xuse-experimental' is deprecated and will be removed in a future release, please use -opt-in instead

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class).all {
        kotlinOptions {
            freeCompilerArgs = freeCompilerArgs + listOf(
                // Avoid having to stutter experimental annotations all over the codebase
                "-Xopt-in=androidx.compose.animation.ExperimentalAnimationApi",
                "-Xopt-in=androidx.compose.material.ExperimentalMaterialApi",
                "-Xopt-in=androidx.compose.runtime.ExperimentalComposeApi",
                "-Xopt-in=androidx.compose.ui.ExperimentalComposeUiApi",
                "-Xopt-in=com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi",
                "-Xopt-in=com.google.accompanist.pager.ExperimentalPagerApi",
                "-Xopt-in=com.google.accompanist.permissions.ExperimentalPermissionsApi",
                "-Xopt-in=kotlin.ExperimentalUnsignedTypes",
                "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
                "-Xopt-in=kotlinx.coroutines.InternalCoroutinesApi"
            )
        }
    }

An alternative to avoid this situation would be to use the @OptIn instead but since only one is allowed per declaration it doesn't work out for my case with multiple experimental features.

You can put multiple experimental features comma separated into @OptIn.

E.g. @OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class, ExperimentalMaterialApi::class)