Declarative framework for sharing UIs across multiple platforms with Kotlin, including Gradle plugin, component libraries, and web support
—
Compose Multiplatform component libraries provide cross-platform UI components and utilities with consistent APIs across all supported platforms. These libraries include resources management, layout components, media support, and development tooling.
Cross-platform resource management with localization support, automatic resource generation, and type-safe access to strings, images, fonts, and other assets.
/**
* String resources with localization support
*/
@Immutable
class StringResource internal constructor(
id: String,
val key: String,
items: Set<ResourceItem>
) : Resource(id, items)
/**
* Load localized string resources
*/
@Composable fun stringResource(resource: StringResource): String
@Composable fun stringResource(resource: StringResource, vararg formatArgs: Any): String
/**
* Suspend functions for non-Composable contexts
*/
suspend fun getString(resource: StringResource): String
suspend fun getString(
environment: ResourceEnvironment,
resource: StringResource
): String
suspend fun getString(
resource: StringResource,
vararg formatArgs: Any
): String
/**
* Drawable resources (images, vectors, SVGs)
*/
@Immutable
class DrawableResource internal constructor(
id: String,
items: Set<ResourceItem>
) : Resource(id, items)
/**
* Load drawable resources as Compose painters and bitmaps
*/
@Composable fun painterResource(resource: DrawableResource): Painter
@Composable fun imageResource(resource: DrawableResource): ImageBitmap
@Composable fun vectorResource(resource: DrawableResource): ImageVector
/**
* Font resources with variation settings
*/
@Immutable
class FontResource internal constructor(
id: String,
items: Set<ResourceItem>
) : Resource(id, items)
/**
* Load font resources (expect/actual implementation)
*/
@Composable expect fun Font(
resource: FontResource,
weight: FontWeight = FontWeight.Normal,
style: FontStyle = FontStyle.Normal,
variationSettings: FontVariation.Settings = FontVariation.Settings(weight, style)
): FontUsage Examples:
// Basic resource usage
@Composable
fun MyScreen() {
val appName = stringResource(Res.string.app_name)
val welcomeMessage = stringResource(Res.string.welcome_message, "User")
val icon = painterResource(Res.drawable.app_icon)
val customFont = Font(Res.font.custom_font, FontWeight.Bold)
Column {
Text(appName, fontFamily = FontFamily(customFont))
Text(welcomeMessage)
Image(painter = icon, contentDescription = "App Icon")
}
}
// Resource configuration in build.gradle.kts
compose {
resources {
publicResClass = false
nameOfResClass = "Res"
generateResClass = auto
packageOfResClass = "com.example.resources"
}
}Handle quantity-based string resources and string arrays.
/**
* Plural string resources following CLDR rules
*/
@Immutable
class PluralStringResource internal constructor(
id: String,
val key: String,
items: Set<ResourceItem>
) : Resource(id, items)
/**
* Load plural strings based on quantity
*/
@Composable fun pluralStringResource(
resource: PluralStringResource,
quantity: Int
): String
@Composable fun pluralStringResource(
resource: PluralStringResource,
quantity: Int,
vararg formatArgs: Any
): String
/**
* String array resources
*/
@Immutable
class StringArrayResource internal constructor(
id: String,
val key: String,
items: Set<ResourceItem>
) : Resource(id, items)
/**
* Load string arrays
*/
@Composable fun stringArrayResource(resource: StringArrayResource): List<String>
suspend fun getStringArray(resource: StringArrayResource): List<String>Control resource resolution based on locale, theme, and device characteristics.
/**
* Environment context for resource resolution
*/
class ResourceEnvironment internal constructor(
internal val language: LanguageQualifier,
internal val region: RegionQualifier,
internal val theme: ThemeQualifier,
internal val density: DensityQualifier
)
/**
* Get current resource environment
*/
@Composable fun rememberResourceEnvironment(): ResourceEnvironment
fun getSystemResourceEnvironment(): ResourceEnvironment
/**
* Resource qualifiers for environment-specific selection
*/
interface Qualifier
class LanguageQualifier(val language: String) : Qualifier
class RegionQualifier(val region: String) : Qualifier
enum class ThemeQualifier : Qualifier {
LIGHT, DARK;
companion object {
fun selectByValue(isDark: Boolean): ThemeQualifier
}
}
enum class DensityQualifier(val dpi: Int) : Qualifier {
LDPI(120), MDPI(160), HDPI(240),
XHDPI(320), XXHDPI(480), XXXHDPI(640);
companion object {
fun selectByValue(dpi: Int): DensityQualifier
fun selectByDensity(density: Float): DensityQualifier
}
}Resizable split pane layouts with customizable splitters and drag handling.
/**
* Vertical split pane with resizable divider
*/
@ExperimentalSplitPaneApi
@Composable fun VerticalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
)
/**
* Horizontal split pane with resizable divider
*/
@ExperimentalSplitPaneApi
@Composable fun HorizontalSplitPane(
modifier: Modifier = Modifier,
splitPaneState: SplitPaneState = rememberSplitPaneState(),
content: SplitPaneScope.() -> Unit
)
/**
* State management for split panes
*/
@ExperimentalSplitPaneApi
class SplitPaneState(
initialPositionPercentage: Float,
moveEnabled: Boolean
) {
/** Whether the splitter can be moved */
var moveEnabled: Boolean
/** Current position as percentage (0.0 to 1.0) */
var positionPercentage: Float
/** Handle raw movement input */
fun dispatchRawMovement(delta: Float)
}
/**
* Remember split pane state
*/
@ExperimentalSplitPaneApi
@Composable fun rememberSplitPaneState(
initialPositionPercentage: Float = 0f,
moveEnabled: Boolean = true
): SplitPaneStateUsage Example:
@OptIn(ExperimentalSplitPaneApi::class)
@Composable
fun SplitPaneExample() {
val splitPaneState = rememberSplitPaneState(initialPositionPercentage = 0.3f)
VerticalSplitPane(
splitPaneState = splitPaneState
) {
first(minSize = 200.dp) {
// Left pane content
LazyColumn {
items(100) { index ->
Text("Item $index")
}
}
}
second(minSize = 300.dp) {
// Right pane content
Text("Detail view")
}
splitter {
visiblePart {
Box(
Modifier
.width(4.dp)
.fillMaxHeight()
.background(Color.Gray)
)
}
handle {
Box(
Modifier
.markAsHandle()
.width(16.dp)
.fillMaxHeight()
.background(Color.Transparent)
)
}
}
}
}Domain-specific language for configuring split pane content and behavior.
/**
* DSL scope for configuring split pane contents
*/
@ExperimentalSplitPaneApi
interface SplitPaneScope {
/** Configure first pane with minimum size */
fun first(minSize: Dp = 0.dp, content: @Composable () -> Unit)
/** Configure second pane with minimum size */
fun second(minSize: Dp = 0.dp, content: @Composable () -> Unit)
/** Configure splitter appearance and behavior */
fun splitter(block: SplitterScope.() -> Unit)
}
/**
* DSL scope for configuring splitter
*/
@ExperimentalSplitPaneApi
interface SplitterScope {
/** Configure visible part of splitter */
fun visiblePart(content: @Composable () -> Unit)
/** Configure drag handle */
fun handle(
alignment: SplitterHandleAlignment = SplitterHandleAlignment.ABOVE,
content: @Composable HandleScope.() -> Unit
)
}
/**
* DSL scope for drag handles
*/
@ExperimentalSplitPaneApi
interface HandleScope {
/** Mark composable as draggable handle */
fun Modifier.markAsHandle(): Modifier
}
/**
* Handle alignment options
*/
@ExperimentalSplitPaneApi
enum class SplitterHandleAlignment {
BEFORE, ABOVE, AFTER
}Cross-platform animated image support for GIF, WebP, and other animated formats.
/**
* Animated image container (expect/actual implementation)
*/
expect class AnimatedImage
/**
* Load animated image from file path
*/
expect suspend fun loadAnimatedImage(path: String): AnimatedImage
/**
* Load animated image from application resources
*/
expect suspend fun loadResourceAnimatedImage(path: String): AnimatedImage
/**
* Animate image and return current frame
*/
@Composable
expect fun AnimatedImage.animate(): ImageBitmap
/**
* Blank image bitmap constant
*/
val ImageBitmap.Companion.Blank: ImageBitmapUsage Example:
@Composable
fun AnimatedImageExample() {
var animatedImage by remember { mutableStateOf<AnimatedImage?>(null) }
LaunchedEffect(Unit) {
animatedImage = loadResourceAnimatedImage("animations/loading.gif")
}
animatedImage?.let { image ->
val currentFrame = image.animate()
Image(
bitmap = currentFrame,
contentDescription = "Loading animation"
)
}
}IDE preview support with parameter injection for development and design workflows.
/**
* Mark Composable functions for IDE preview
*/
@MustBeDocumented
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.FUNCTION)
@Repeatable
annotation class Preview
/**
* Inject sample data into Preview function parameters
*/
annotation class PreviewParameter(
val provider: KClass<out PreviewParameterProvider<*>>,
val limit: Int = Int.MAX_VALUE
)
/**
* Provide sample data for preview parameters (expect/actual)
*/
expect interface PreviewParameterProvider<T> {
val values: Sequence<T>
val count: Int
}Usage Example:
// Sample data provider
class UserProvider : PreviewParameterProvider<User> {
override val values = sequenceOf(
User("Alice", 25),
User("Bob", 30),
User("Charlie", 35)
)
}
// Preview with parameter injection
@Preview
@Composable
fun UserCardPreview(@PreviewParameter(UserProvider::class) user: User) {
UserCard(user = user)
}
// Multiple preview configurations
@Preview(name = "Light Theme")
@Preview(name = "Dark Theme")
@Composable
fun ProfileScreenPreview() {
ProfileScreen()
}/**
* Marks experimental resource APIs
*/
@RequiresOptIn("This API is experimental and is likely to change in the future.")
annotation class ExperimentalResourceApi
/**
* Marks experimental split pane APIs
*/
@RequiresOptIn(level = RequiresOptIn.Level.WARNING)
annotation class ExperimentalSplitPaneApi
/**
* Marks internal resource APIs
*/
@RequiresOptIn("This is internal API of the Compose gradle plugin.")
annotation class InternalResourceApiInstall with Tessl CLI
npx tessl i tessl/maven-compose-multiplatform