Resource management library for Compose Multiplatform applications providing type-safe access to images, strings, fonts, and drawable assets across all platforms.
—
The Resource Environment system manages the context for resource selection, including locale (language and region), theme (light/dark), and screen density. This enables automatic selection of the most appropriate resource variant for the current device and user preferences.
class ResourceEnvironment(
internal val language: LanguageQualifier,
internal val region: RegionQualifier,
internal val theme: ThemeQualifier,
internal val density: DensityQualifier
)The ResourceEnvironment encapsulates all the factors that influence resource selection. It provides a complete context for determining which specific resource variant to load from the available options.
@Composable
fun rememberResourceEnvironment(): ResourceEnvironmentCreates and remembers a ResourceEnvironment based on the current Compose context. This automatically tracks changes in system locale, theme, and density, updating resources as needed.
Usage:
@Composable
fun LocalizedContent() {
val environment = rememberResourceEnvironment()
// Environment automatically reflects:
// - Current system locale (language + region)
// - System theme (light/dark mode)
// - Screen density (MDPI, HDPI, XHDPI, etc.)
LaunchedEffect(environment) {
// React to environment changes
updateContentForEnvironment(environment)
}
}fun getSystemResourceEnvironment(): ResourceEnvironmentProvides the resource environment for non-Composable contexts. This is an expensive operation and should be used sparingly, preferably with caching.
Usage:
suspend fun loadSystemResources() {
val environment = getSystemResourceEnvironment()
val welcomeMessage = getString(environment, Res.string.welcome)
// Use environment for multiple resource loads
val appName = getString(environment, Res.string.app_name)
val version = getString(environment, Res.string.version_info)
}
class BackgroundService {
private var cachedEnvironment: ResourceEnvironment? = null
suspend fun processWithResources() {
val environment = cachedEnvironment ?: getSystemResourceEnvironment().also {
cachedEnvironment = it
}
val statusMessage = getString(environment, Res.string.processing_status)
updateNotification(statusMessage)
}
}class LanguageQualifier(val language: String) : QualifierRepresents the language component of the locale (ISO 639-1 language codes).
Common Language Codes:
"en" - English"es" - Spanish"fr" - French"de" - German"ja" - Japanese"zh" - Chinese"ar" - Arabicclass RegionQualifier(val region: String) : QualifierRepresents the region/country component of the locale (ISO 3166-1 alpha-2 country codes).
Common Region Codes:
"US" - United States"GB" - United Kingdom"CA" - Canada"FR" - France"DE" - Germany"JP" - Japan"CN" - Chinaenum class ThemeQualifier : Qualifier {
LIGHT,
DARK;
companion object {
fun selectByValue(isDark: Boolean): ThemeQualifier
}
}Represents the system theme preference for light or dark mode.
Usage:
val currentTheme = ThemeQualifier.selectByValue(isSystemInDarkTheme())
// Manual theme selection
val lightTheme = ThemeQualifier.LIGHT
val darkTheme = ThemeQualifier.DARKenum class DensityQualifier(val dpi: Int) : Qualifier {
LDPI(120), // Low density
MDPI(160), // Medium density (baseline)
HDPI(240), // High density
XHDPI(320), // Extra high density
XXHDPI(480), // Extra extra high density
XXXHDPI(640); // Extra extra extra high density
companion object {
fun selectByValue(dpi: Int): DensityQualifier
fun selectByDensity(density: Float): DensityQualifier
}
}Represents the screen density for selecting appropriate image and layout resources.
Density Selection:
// From DPI value
val density1 = DensityQualifier.selectByValue(320) // Returns XHDPI
// From density multiplier
val density2 = DensityQualifier.selectByDensity(2.0f) // Returns XHDPIThe library uses a sophisticated algorithm to select the best matching resource:
Locale matching (language + region)
Theme matching
Density matching
Available Resources:
drawable/icon.png # Default (MDPI)
drawable-hdpi/icon.png # High density
drawable-xhdpi/icon.png # Extra high density
drawable-night/icon.png # Dark theme default
drawable-night-hdpi/icon.png # Dark theme high densitySelection for XHDPI + Dark Theme:
drawable-night-xhdpi/icon.png (exact match) - Not availabledrawable-night-hdpi/icon.png (theme + lower density) - Selected// Create specific environment for testing
val testEnvironment = ResourceEnvironment(
language = LanguageQualifier("es"),
region = RegionQualifier("MX"),
theme = ThemeQualifier.DARK,
density = DensityQualifier.XXHDPI
)
// Use for resource loading
suspend fun loadSpanishResources(): String {
return getString(testEnvironment, Res.string.welcome_message)
}@Composable
fun EnvironmentAwareContent() {
val environment = rememberResourceEnvironment()
// React to specific environment changes
LaunchedEffect(environment.theme) {
// Theme changed
updateThemeSpecificContent(environment.theme)
}
LaunchedEffect(environment.language, environment.region) {
// Locale changed
updateLocalizedContent(environment.language, environment.region)
}
}fun environmentChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {
return old.language != new.language ||
old.region != new.region ||
old.theme != new.theme ||
old.density != new.density
}
// Track specific changes
fun localeChanged(old: ResourceEnvironment, new: ResourceEnvironment): Boolean {
return old.language != new.language || old.region != new.region
}Configuration.uiModeLocale.getDefault()NSLocale.currentLocaleUITraitCollectionnavigator.languageclass EnvironmentCache {
private var cachedEnvironment: ResourceEnvironment? = null
private var lastCheck: Long = 0
private val cacheTimeout = 5000L // 5 seconds
fun getCurrentEnvironment(): ResourceEnvironment {
val now = System.currentTimeMillis()
if (cachedEnvironment == null || now - lastCheck > cacheTimeout) {
cachedEnvironment = getSystemResourceEnvironment()
lastCheck = now
}
return cachedEnvironment!!
}
}suspend fun loadMultiLanguageContent(): Map<String, String> {
val languages = listOf("en", "es", "fr", "de")
val content = mutableMapOf<String, String>()
languages.forEach { lang ->
val environment = ResourceEnvironment(
LanguageQualifier(lang),
RegionQualifier(""),
ThemeQualifier.LIGHT,
DensityQualifier.MDPI
)
content[lang] = getString(environment, Res.string.app_description)
}
return content
}class ResourcePreloader {
suspend fun preloadForEnvironment(environment: ResourceEnvironment) {
// Preload commonly used resources for this environment
val commonResources = listOf(
Res.string.app_name,
Res.string.loading,
Res.string.error_message,
Res.drawable.app_icon
)
commonResources.forEach { resource ->
when (resource) {
is StringResource -> getString(environment, resource)
is DrawableResource -> getDrawableResourceBytes(environment, resource)
}
}
}
}// Create test environments for different scenarios
val testEnvironments = mapOf(
"US English" to ResourceEnvironment(
LanguageQualifier("en"), RegionQualifier("US"),
ThemeQualifier.LIGHT, DensityQualifier.XHDPI
),
"Spanish Mexico" to ResourceEnvironment(
LanguageQualifier("es"), RegionQualifier("MX"),
ThemeQualifier.DARK, DensityQualifier.XXHDPI
),
"French Canada" to ResourceEnvironment(
LanguageQualifier("fr"), RegionQualifier("CA"),
ThemeQualifier.LIGHT, DensityQualifier.HDPI
)
)
// Test resource selection
testEnvironments.forEach { (name, environment) ->
val text = getString(environment, Res.string.welcome_message)
println("$name: $text")
}fun validateResourceCoverage(
environments: List<ResourceEnvironment>,
resources: List<StringResource>
) {
environments.forEach { environment ->
resources.forEach { resource ->
try {
val text = getString(environment, resource)
println("✓ ${resource.key} available for $environment")
} catch (e: Exception) {
println("✗ ${resource.key} missing for $environment: ${e.message}")
}
}
}
}Cache environment objects when used frequently:
@Composable
fun OptimizedContent() {
val environment = rememberResourceEnvironment()
// Environment is automatically cached and updated by Compose
}Use appropriate environment access:
// In Composables - use rememberResourceEnvironment()
@Composable
fun MyComposable() {
val env = rememberResourceEnvironment()
}
// Outside Composables - use getSystemResourceEnvironment() sparingly
suspend fun backgroundTask() {
val env = getSystemResourceEnvironment()
}Test with multiple environments:
Handle environment changes gracefully:
@Composable
fun AdaptiveContent() {
val environment = rememberResourceEnvironment()
// Content adapts automatically to environment changes
val title = stringResource(Res.string.title)
val icon = painterResource(Res.drawable.icon)
// Resources are automatically reloaded when environment changes
}Provide appropriate fallbacks:
Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-compose-components--components-resources