Tree composition support and runtime infrastructure for Compose Multiplatform desktop applications with declarative UI development APIs.
—
Core composition APIs for building composable functions and managing implicit context propagation through the composition tree. These APIs form the foundation of the Compose runtime system.
The fundamental building blocks of Compose UI.
/**
* Marks functions as composable, enabling them to call other composable functions
* and participate in the composition process
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.BINARY)
annotation class ComposableUsage Examples:
import androidx.compose.runtime.Composable
// Basic composable function
@Composable
fun Greeting(name: String) {
Text("Hello, $name!")
}
// Composable with parameters and state
@Composable
fun Counter(initialValue: Int = 0) {
var count by remember { mutableStateOf(initialValue) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
// Composable that accepts other composables
@Composable
fun Card(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Surface(modifier = modifier) {
content()
}
}Remember values across recompositions with the remember API.
/**
* Remembers a value across recompositions
* The value is recalculated only on first composition or when keys change
* @param calculation Function to create the remembered value
* @return The remembered value
*/
@Composable
fun <T> remember(calculation: () -> T): T
/**
* Remembers a value with a single dependency key
* @param key1 Dependency key - value is recalculated when this changes
* @param calculation Function to create the remembered value
* @return The remembered value
*/
@Composable
fun <T> remember(key1: Any?, calculation: () -> T): T
/**
* Remembers a value with multiple dependency keys
*/
@Composable
fun <T> remember(key1: Any?, key2: Any?, calculation: () -> T): T
@Composable
fun <T> remember(key1: Any?, key2: Any?, key3: Any?, calculation: () -> T): T
/**
* Remembers a value with variable number of dependency keys
*/
@Composable
fun <T> remember(vararg keys: Any?, calculation: () -> T): TUsage Examples:
@Composable
fun RememberExample(userId: String, settings: AppSettings) {
// Remember expensive computation
val processedData = remember(userId) {
expensiveDataProcessing(userId)
}
// Remember stateful objects
val listState = remember { LazyListState() }
val focusRequester = remember { FocusRequester() }
// Remember with multiple keys
val cachedResult = remember(userId, settings.cacheEnabled) {
if (settings.cacheEnabled) {
getCachedData(userId)
} else {
getServerData(userId)
}
}
// Remember lambda functions to avoid recreating them
val onItemClick = remember(userId) {
{ itemId: String -> handleItemClick(userId, itemId) }
}
}Provide implicit values that can be accessed by any composable in the subtree.
/**
* Provides values to CompositionLocal instances for the content block
* @param values Provided values as key-value pairs
* @param content Composable content that can access the provided values
*/
@Composable
fun CompositionLocalProvider(
vararg values: ProvidedValue<*>,
content: @Composable () -> Unit
)
/**
* Abstract base class for CompositionLocal
*/
abstract class CompositionLocal<T> {
/**
* Current value of this CompositionLocal
*/
val current: T
@Composable get
/**
* Provides a value for this CompositionLocal
*/
infix fun provides(value: T): ProvidedValue<T>
}
/**
* CompositionLocal that never changes - more efficient for static values
*/
abstract class StaticProvidableCompositionLocal<T> : CompositionLocal<T>()
/**
* CompositionLocal that can change and notifies composables when it does
*/
abstract class DynamicProvidableCompositionLocal<T> : CompositionLocal<T>()
/**
* Base class for CompositionLocal instances that can provide values
*/
abstract class ProvidableCompositionLocal<T> : CompositionLocal<T>()
/**
* Represents a key-value pair for providing CompositionLocal values
*/
data class ProvidedValue<T>(
val compositionLocal: CompositionLocal<T>,
val value: T
)Usage Examples:
// Define composition locals
val LocalUserPreferences = compositionLocalOf { UserPreferences() }
val LocalTheme = staticCompositionLocalOf { AppTheme.Light }
@Composable
fun App() {
val userPrefs = remember { loadUserPreferences() }
val currentTheme = remember(userPrefs.themeMode) {
getThemeForMode(userPrefs.themeMode)
}
CompositionLocalProvider(
LocalUserPreferences provides userPrefs,
LocalTheme provides currentTheme
) {
MainScreen()
}
}
@Composable
fun MainScreen() {
// Access composition locals
val theme = LocalTheme.current
val userPrefs = LocalUserPreferences.current
Surface(color = theme.backgroundColor) {
if (userPrefs.showWelcome) {
WelcomeMessage()
}
ContentArea()
}
}
@Composable
fun WelcomeMessage() {
val userPrefs = LocalUserPreferences.current
Text(
text = "Welcome, ${userPrefs.userName}!",
color = LocalTheme.current.textColor
)
}Define your own CompositionLocal instances.
/**
* Creates a CompositionLocal with change notifications
* More overhead but allows dynamic updates
* @param policy Mutation policy for detecting changes
* @param defaultFactory Optional factory for default value
* @return ProvidableComposionLocal instance
*/
fun <T> compositionLocalOf(
policy: SnapshotMutationPolicy<T> = structuralEqualityPolicy(),
defaultFactory: (() -> T)? = null
): ProvidableCompositionLocal<T>
/**
* Creates a CompositionLocal that never changes
* More efficient for static values
* @param defaultFactory Optional factory for default value
* @return ProvidableCompositionLocal instance
*/
fun <T> staticCompositionLocalOf(
defaultFactory: (() -> T)? = null
): ProvidableCompositionLocal<T>Usage Examples:
// Dynamic composition local (can change)
val LocalUser = compositionLocalOf<User?> { null }
// Static composition local (never changes)
val LocalAppConfig = staticCompositionLocalOf {
AppConfig.default()
}
// Composition local with default value
val LocalAnalytics = compositionLocalOf {
NoOpAnalytics() // Default implementation
}
@Composable
fun MyApp() {
val currentUser = remember { getCurrentUser() }
CompositionLocalProvider(
LocalUser provides currentUser,
LocalAnalytics provides RealAnalytics()
) {
NavigationHost()
}
}
@Composable
fun UserProfile() {
val user = LocalUser.current
val analytics = LocalAnalytics.current
if (user != null) {
LaunchedEffect(user.id) {
analytics.trackScreenView("UserProfile", user.id)
}
Column {
Text("Name: ${user.name}")
Text("Email: ${user.email}")
}
} else {
LoginPrompt()
}
}Access and manipulate the composition tree structure.
/**
* Returns the current Composer instance
*/
val currentComposer: Composer
@Composable get
/**
* Returns the current recompose scope
*/
val currentRecomposeScope: RecomposeScope
@Composable get
/**
* Interface for controlling recomposition
*/
interface RecomposeScope {
/**
* Invalidates this scope, causing it to recompose
*/
fun invalidate()
}
/**
* The Composer is the interface between the composable functions and the composition tree
*/
interface Composer {
/**
* Whether the composer is currently inserting content into the composition
*/
val inserting: Boolean
/**
* Whether the composer is currently skipping content during recomposition
*/
val skipping: Boolean
}Usage Examples:
@Composable
fun DebuggingExample() {
val scope = currentRecomposeScope
Button(
onClick = {
// Force recomposition of this scope
scope.invalidate()
}
) {
Text("Force Recompose")
}
}
@Composable
fun ComposerExample() {
val composer = currentComposer
// Access composer information (advanced usage)
SideEffect {
println("Composer inserting: ${composer.inserting}")
println("Composer skipping: ${composer.skipping}")
}
}Control composition behavior with annotations.
/**
* Marks composables that shouldn't restart on recomposition
* Use for performance optimization when restart isn't needed
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class NonRestartableComposable
/**
* Marks composables that only read values and don't participate in recomposition
* Used for optimization - these composables won't be recomposed
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class ReadOnlyComposable
/**
* Marks APIs used by the Compose compiler
* These are internal APIs not intended for direct use
*/
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY,
AnnotationTarget.TYPEALIAS
)
@Retention(AnnotationRetention.BINARY)
annotation class ComposeCompilerApiUsage Examples:
// Non-restartable composable for performance
@NonRestartableComposable
@Composable
fun StaticHeader() {
Text("App Title") // Never changes, doesn't need restart
}
// Read-only composable that doesn't trigger recomposition
@ReadOnlyComposable
@Composable
fun currentPlatform(): Platform {
return Platform.current // Static value
}
@Composable
fun OptimizedScreen() {
StaticHeader() // Won't restart on recomposition
val platform = currentPlatform() // Won't cause recomposition
PlatformSpecificContent(platform)
}Create custom remember-like behavior.
@Composable
fun <T> rememberWithLifecycle(
lifecycle: Lifecycle,
calculation: () -> T
): T {
return remember(lifecycle.currentState) {
calculation()
}
}
@Composable
fun <T> rememberCached(
key: String,
calculation: () -> T
): T {
return remember(key) {
cache.get(key) ?: calculation().also { cache.put(key, it) }
}
}Patterns for conditional composition based on state.
@Composable
fun ConditionalComposition(showDetails: Boolean) {
if (showDetails) {
DetailedView()
} else {
SummaryView()
}
}
@Composable
fun <T> ConditionalProvider(
condition: Boolean,
value: T,
content: @Composable () -> Unit
) {
if (condition) {
CompositionLocalProvider(LocalMyValue provides value) {
content()
}
} else {
content()
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-runtime--runtime-desktop