Jetpack Compose中使用CompositionLocal全局共享Assets配置遇Context获取问题,求可行替代方案
Great question! The core issue here is that the lambda used to initialize compositionLocalOf runs outside a Composable context—so LocalContext.current isn't accessible at that point. Let's walk through two reliable approaches to fix this and get your config working across your app:
Approach 1: Preload Configuration in Application (Simplest for Static Config)
Since your config is a static JSON in Assets, you can initialize it once when your app starts, then inject it into CompositionLocal.
Step 1: Update Your Configuration Class
Keep your existing Configuration object, but ensure we can initialize it safely early on:
object Configuration { lateinit var branding: Branding fun init(context: Context) { val gson = GsonBuilder().create() // Use `use` to auto-close the input stream context.assets.open("branding.json").use { inputStream -> branding = gson.fromJson(InputStreamReader(inputStream), Branding::class.java) } } }
Step 2: Initialize in Your Application Class
Load the config when your app launches (this runs before any Composables are created):
class MyApp : Application() { override fun onCreate() { super.onCreate() Configuration.init(this) } }
Don't forget to register this MyApp class in your AndroidManifest.xml.
Step 3: Provide the Config via CompositionLocal
Define your CompositionLocal and provide it at the root of your Compose tree:
// Define the CompositionLocal (no default init here—we'll provide it explicitly) val LocalAppConfiguration = compositionLocalOf<Configuration> { error("LocalAppConfiguration must be provided via CompositionLocalProvider") } // In your Activity's setContent: setContent { CompositionLocalProvider(LocalAppConfiguration provides Configuration) { // Your app's root composable here MyMainScreen() } }
Step 4: Use the Config Anywhere in Compose
Now you can access the config in any child composable:
@Composable fun MyMainScreen() { val config = LocalAppConfiguration.current Text(text = config.branding.appName) }
Approach 2: Lazy-Load in Composable (For Dynamic/Async Loading)
If you prefer to load the config within the Compose lifecycle (e.g., for dynamic updates or async loading), use a state holder with remember.
Step 1: Create a State Holder Class
This class manages the config state and handles loading:
class AppConfigState(private val context: Context) { // Use mutableStateOf to trigger recompositions when the config loads var branding by mutableStateOf<Branding?>(null) private set init { // Load sync (safe for assets; use async if loading from network) loadConfig() } private fun loadConfig() { val gson = GsonBuilder().create() context.assets.open("branding.json").use { inputStream -> branding = gson.fromJson(InputStreamReader(inputStream), Branding::class.java) } } // For async loading (e.g., network), use this suspend function instead: private suspend fun loadConfigAsync() { withContext(Dispatchers.IO) { val gson = GsonBuilder().create() context.assets.open("branding.json").use { inputStream -> val loadedBranding = gson.fromJson(InputStreamReader(inputStream), Branding::class.java) withContext(Dispatchers.Main) { branding = loadedBranding } } } } }
Step 2: Provide the State via CompositionLocal
Initialize the state in your root composable and provide it:
val LocalAppConfigState = compositionLocalOf<AppConfigState> { error("LocalAppConfigState must be provided") } setContent { val context = LocalContext.current // Use remember to retain the state across recompositions val configState = remember { AppConfigState(context) } // For async loading, add a LaunchedEffect to trigger the suspend function: // LaunchedEffect(Unit) { configState.loadConfigAsync() } CompositionLocalProvider(LocalAppConfigState provides configState) { MyMainScreen() } }
Step 3: Use the Config in Composables
Handle the nullable state (since loading might take a moment):
@Composable fun MyMainScreen() { val configState = LocalAppConfigState.current configState.branding?.let { branding -> Text(text = branding.appName) } ?: run { // Show a loading indicator while config loads CircularProgressIndicator() } }
内容的提问来源于stack exchange,提问作者Thomas Cirksena

