DEV Community

myougaTheAxo
myougaTheAxo

Posted on

CompositionLocal Complete Guide — Theming/DI/Implicit Parameter Passing

What You'll Learn

This article explains CompositionLocal (staticCompositionLocalOf, compositionLocalOf, custom Provider, theme integration, testing).


What is CompositionLocal

A mechanism to implicitly pass data between Composables, avoiding explicit parameter threading.

// Built-in CompositionLocal examples
@Composable
fun Example() {
    val context = LocalContext.current          // Context
    val density = LocalDensity.current          // Density
    val config = LocalConfiguration.current     // Configuration
    val lifecycle = LocalLifecycleOwner.current // LifecycleOwner
}
Enter fullscreen mode Exit fullscreen mode

Custom CompositionLocal

// staticCompositionLocalOf: for values that rarely change (wider recomposition)
val LocalAppConfig = staticCompositionLocalOf<AppConfig> {
    error("No AppConfig provided")
}

// compositionLocalOf: for values that change frequently (narrow recomposition)
val LocalUserSession = compositionLocalOf<UserSession?> { null }

data class AppConfig(
    val apiBaseUrl: String,
    val isDebug: Boolean,
    val appVersion: String
)

data class UserSession(
    val userId: String,
    val displayName: String,
    val isAdmin: Boolean
)
Enter fullscreen mode Exit fullscreen mode

Provider

@Composable
fun App() {
    val appConfig = AppConfig(
        apiBaseUrl = BuildConfig.API_URL,
        isDebug = BuildConfig.DEBUG,
        appVersion = BuildConfig.VERSION_NAME
    )
    val userSession = remember { mutableStateOf<UserSession?>(null) }

    CompositionLocalProvider(
        LocalAppConfig provides appConfig,
        LocalUserSession provides userSession.value
    ) {
        AppNavigation()
    }
}

// Access in child Composables
@Composable
fun SettingsScreen() {
    val config = LocalAppConfig.current
    val session = LocalUserSession.current

    Column(Modifier.padding(16.dp)) {
        Text("Version: ${config.appVersion}")
        session?.let { Text("User: ${it.displayName}") }
    }
}
Enter fullscreen mode Exit fullscreen mode

Custom Theme Values

@Immutable
data class AppSpacing(
    val small: Dp = 4.dp,
    val medium: Dp = 8.dp,
    val large: Dp = 16.dp,
    val extraLarge: Dp = 24.dp
)

val LocalSpacing = staticCompositionLocalOf { AppSpacing() }

// Use as MaterialTheme extension
@Composable
fun AppTheme(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalSpacing provides AppSpacing()) {
        MaterialTheme(content = content)
    }
}

// Accessor extension property
val MaterialTheme.spacing: AppSpacing
    @Composable @ReadOnlyComposable
    get() = LocalSpacing.current

// Usage
@Composable
fun MyScreen() {
    Column(Modifier.padding(MaterialTheme.spacing.large)) {
        Text("Hello", Modifier.padding(bottom = MaterialTheme.spacing.medium))
    }
}
Enter fullscreen mode Exit fullscreen mode

Testing

@Test
fun testWithCustomCompositionLocal() {
    val testConfig = AppConfig(
        apiBaseUrl = "https://test.api.com",
        isDebug = true,
        appVersion = "1.0.0-test"
    )

    composeTestRule.setContent {
        CompositionLocalProvider(LocalAppConfig provides testConfig) {
            SettingsScreen()
        }
    }

    composeTestRule.onNodeWithText("Version: 1.0.0-test").assertIsDisplayed()
}
Enter fullscreen mode Exit fullscreen mode

Summary

API Use Case
compositionLocalOf Frequently changing values
staticCompositionLocalOf Rarely changing values
CompositionLocalProvider Provide values
.current Access values
  • staticCompositionLocalOf recomposes entire subtree when changed
  • compositionLocalOf recomposes only reading locations
  • Perfect for theme extensions (Spacing, Elevation, etc.)
  • Use CompositionLocalProvider for testing

8 production-ready Android app templates (custom theme support) are available.

Browse templatesGumroad

Related articles:

  • Material3 theming
  • Dark mode
  • Custom Composables

Ready-Made Android App Templates

8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.

Browse templatesGumroad

Top comments (0)