CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-jetbrains-compose-runtime--runtime-uikitx64

Compose Multiplatform runtime library for iOS UIKit x64 target providing core runtime functionality for declarative UI framework integration.

Pending
Overview
Eval results
Files

composition-local.mddocs/

CompositionLocal

Context-like data flow mechanism for passing data implicitly down the composition tree without explicit parameter passing. CompositionLocal enables sharing data across the entire composition hierarchy while maintaining type safety.

Capabilities

CompositionLocal Creation

Functions for creating CompositionLocal instances with different update policies.

/**
 * Creates a CompositionLocal that uses structural equality for change detection
 * @param defaultFactory Optional factory for default value when no provider is found
 * @return ProvidableCompositionLocal instance
 */
fun <T> compositionLocalOf(defaultFactory: (() -> T)? = null): ProvidableCompositionLocal<T>

/**
 * Creates a CompositionLocal that uses referential equality for change detection
 * Optimized for values that rarely change
 * @param defaultFactory Optional factory for default value when no provider is found
 * @return ProvidableCompositionLocal instance  
 */
fun <T> staticCompositionLocalOf(defaultFactory: (() -> T)? = null): ProvidableCompositionLocal<T>

Usage Examples:

// Theme CompositionLocal
val LocalAppTheme = compositionLocalOf<AppTheme> { 
    error("No theme provided") 
}

// User CompositionLocal with default
val LocalCurrentUser = compositionLocalOf { User.guest() }

// Static CompositionLocal for rarely-changing configuration
val LocalAppConfiguration = staticCompositionLocalOf {
    AppConfiguration.default()
}

data class AppTheme(
    val colors: ColorScheme,
    val typography: Typography,
    val spacing: Spacing
)

data class User(val id: String, val name: String) {
    companion object {
        fun guest() = User("guest", "Guest User")
    }
}

CompositionLocal Classes

Core classes and interfaces for the CompositionLocal system.

/**
 * Base class for CompositionLocal providing read access
 * @param T The type of value provided by this CompositionLocal
 */
abstract class CompositionLocal<T>(defaultFactory: (() -> T)?) {
    /**
     * Current value of this CompositionLocal in the composition
     * Throws if no provider is found and no default factory was provided
     */
    val current: T
        @Composable get
    
    /**
     * Current value or null if no provider is found
     */
    val currentOrNull: T?
        @Composable get
}

/**
 * CompositionLocal that can provide values to child compositions
 */
abstract class ProvidableCompositionLocal<T> : CompositionLocal<T> {
    /**
     * Creates a ProvidedValue for use with CompositionLocalProvider
     * @param value The value to provide
     * @return ProvidedValue instance
     */
    infix fun provides(value: T): ProvidedValue<T>
    
    /**
     * Creates a ProvidedValue with a computed value
     * @param value Function that computes the value
     * @return ProvidedValue instance
     */
    infix fun providesDefault(value: () -> T): ProvidedValue<T>
}

/**
 * Represents a value provided to a CompositionLocal
 */
class ProvidedValue<T> internal constructor(
    val compositionLocal: CompositionLocal<T>,
    val value: T,
    val canOverride: Boolean
)

CompositionLocalProvider

Function for providing values to CompositionLocals.

/**
 * Provides values to CompositionLocals for the content lambda
 * @param values Variable number of ProvidedValue instances
 * @param content Composable content that can access the provided values
 */
@Composable
fun CompositionLocalProvider(
    vararg values: ProvidedValue<*>,
    content: @Composable () -> Unit
): Unit

Usage Examples:

@Composable
fun App() {
    val theme = remember { AppTheme.default() }
    val user = remember { getCurrentUser() }
    
    CompositionLocalProvider(
        LocalAppTheme provides theme,
        LocalCurrentUser provides user
    ) {
        MainScreen() // Can access theme and user via CompositionLocal
    }
}

@Composable
fun MainScreen() {
    // Access provided values
    val theme = LocalAppTheme.current
    val user = LocalCurrentUser.current
    
    Column(
        modifier = Modifier.background(theme.colors.background)
    ) {
        Text(
            text = "Welcome, ${user.name}!",
            style = theme.typography.headlineMedium,
            color = theme.colors.onBackground
        )
        
        UserContent() // Can also access the same CompositionLocals
    }
}

Reading CompositionLocal Values

Methods for safely accessing CompositionLocal values.

/**
 * Extension property to get current value (same as .current)
 */
val <T> CompositionLocal<T>.current: T
    @Composable get

/**
 * Extension property to get current value or null if not provided
 */
val <T> CompositionLocal<T>.currentOrNull: T?
    @Composable get

Usage Examples:

@Composable
fun SafeCompositionLocalAccess() {
    // Safe access with null check
    val user = LocalCurrentUser.currentOrNull
    
    if (user != null) {
        Text("Hello, ${user.name}")
    } else {
        Text("Please log in")
    }
    
    // Direct access (throws if not provided and no default)
    val theme = LocalAppTheme.current
    
    // Using in conditional logic
    val isGuest = LocalCurrentUser.currentOrNull?.id == "guest"
}

Common CompositionLocal Patterns

Nested Providers

@Composable
fun NestedProvidersExample() {
    val lightTheme = AppTheme.light()
    
    CompositionLocalProvider(LocalAppTheme provides lightTheme) {
        Column {
            Text("Light theme content")
            
            // Override theme for specific section
            val darkTheme = AppTheme.dark()
            CompositionLocalProvider(LocalAppTheme provides darkTheme) {
                Text("Dark theme content")
                
                NestedContent() // Uses dark theme
            }
            
            Text("Back to light theme")
        }
    }
}

Dynamic Value Updates

@Composable
fun DynamicCompositionLocalExample() {
    var isDarkMode by remember { mutableStateOf(false) }
    
    val theme = if (isDarkMode) AppTheme.dark() else AppTheme.light()
    
    CompositionLocalProvider(LocalAppTheme provides theme) {
        Column {
            Switch(
                checked = isDarkMode,
                onCheckedChange = { isDarkMode = it }
            )
            
            ThemedContent() // Automatically updates when theme changes
        }
    }
}

Custom CompositionLocal Scope

// Custom scope for feature-specific data
val LocalFeatureConfig = compositionLocalOf<FeatureConfig> { 
    FeatureConfig.default() 
}

@Composable
fun FeatureScope(
    config: FeatureConfig,
    content: @Composable () -> Unit
) {
    CompositionLocalProvider(LocalFeatureConfig provides config) {
        content()
    }
}

@Composable
fun FeatureScreen() {
    FeatureScope(config = FeatureConfig.forUser(currentUser)) {
        FeatureContent() // Can access LocalFeatureConfig
    }
}

iOS Platform Integration

iOS-Specific CompositionLocals

// iOS-specific configuration
val LocalIOSConfiguration = compositionLocalOf<IOSConfiguration> {
    error("iOS configuration not provided")
}

val LocalUIViewController = compositionLocalOf<UIViewController?> { null }

data class IOSConfiguration(
    val statusBarStyle: UIStatusBarStyle,
    val interfaceOrientation: UIInterfaceOrientation,
    val safeAreaInsets: EdgeInsets
)

@Composable
fun IOSApp(viewController: UIViewController) {
    val iosConfig = remember {
        IOSConfiguration(
            statusBarStyle = UIStatusBarStyle.default,
            interfaceOrientation = UIInterfaceOrientation.portrait,
            safeAreaInsets = viewController.view.safeAreaInsets.toEdgeInsets()
        )
    }
    
    CompositionLocalProvider(
        LocalUIViewController provides viewController,
        LocalIOSConfiguration provides iosConfig
    ) {
        AppContent()
    }
}

Integration with UIKit

@Composable
fun UIKitIntegrationExample() {
    val viewController = LocalUIViewController.currentOrNull
    val iosConfig = LocalIOSConfiguration.current
    
    LaunchedEffect(iosConfig.statusBarStyle) {
        viewController?.setNeedsStatusBarAppearanceUpdate()
    }
    
    // Use iOS configuration in Composable
    Column(
        modifier = Modifier.padding(iosConfig.safeAreaInsets)
    ) {
        // Content respects safe area insets
    }
}

Advanced Patterns

CompositionLocal Transformation

@Composable
fun TransformCompositionLocalExample() {
    val baseTheme = LocalAppTheme.current
    
    // Create derived theme
    val dialogTheme = remember(baseTheme) {
        baseTheme.copy(
            colors = baseTheme.colors.copy(
                surface = baseTheme.colors.surfaceVariant
            )
        )
    }
    
    CompositionLocalProvider(LocalAppTheme provides dialogTheme) {
        DialogContent() // Uses modified theme
    }
}

Conditional Provision

@Composable
fun ConditionalProvisionExample(user: User?) {
    if (user != null) {
        CompositionLocalProvider(LocalCurrentUser provides user) {
            AuthenticatedContent()
        }
    } else {
        UnauthenticatedContent() // LocalCurrentUser not provided
    }
}

Multiple Providers with Same Local

@Composable
fun MultipleProvidersExample() {
    val adminTheme = AppTheme.admin()
    val userTheme = AppTheme.user()
    
    Row {
        CompositionLocalProvider(LocalAppTheme provides adminTheme) {
            AdminPanel()
        }
        
        CompositionLocalProvider(LocalAppTheme provides userTheme) {
            UserPanel()
        }
    }
}

Performance Considerations

  • Static vs Dynamic: Use staticCompositionLocalOf for rarely-changing values to optimize recomposition
  • Scope Minimization: Provide CompositionLocal values as close as possible to where they're needed
  • Value Stability: Ensure provided values are stable to avoid unnecessary recomposition
  • Default Factories: Use default factories sparingly as they're called on every access when no provider exists
  • Nested Providers: Minimize deep nesting of providers to reduce composition overhead

Install with Tessl CLI

npx tessl i tessl/maven-org-jetbrains-compose-runtime--runtime-uikitx64

docs

composition-lifecycle.md

composition-local.md

effects.md

index.md

ios-integration.md

snapshot-system.md

state-management.md

tile.json