? ? ? ? 這一節主要了解一下CompositionLocal,CompositionLocal是Jetpack Compose中用于組件樹內隱式數據傳遞的核心機制,其設計初衷是解決跨多層組件的數據共享問題,避免通過函數參數逐層傳遞數據。簡單總結:
API:
(1)compositionLocalOf<T>
創建一個動態的CompositionLocal實例,當值變化時,僅觸發讀取該值的組件重組。
(2)staticCompositionLocalOf<T>
創建一個靜態的CompositionLocal實例,值變化時觸發整個作用域內所有組件重組。?
(3)CompositionLocalProvider
為子組件樹提供CompositionLocal的值,作用域僅限于其內容塊。?
場景:
1. 主題與樣式配置,最常見的場景是傳遞應用主題信息(顏色、字體、間距等),讓組件樹中的所有子組件都能訪問統一的樣式配置。
2. 上下文信息傳遞,替代傳統Android中的Context傳遞,在Compose組件樹中共享上下文相關數據。?
3. 用戶狀態與全局配置,共享用戶信息(如登錄狀態、權限)或全局配置,讓深層嵌套的組件也能訪問這些狀態。
4. 工具類與服務共享,傳遞工具類實例或服務,避免在每個組件中單獨初始化。
栗子:
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.spprivate val LocalAppTheme = compositionLocalOf<AppTheme> {error("No AppTheme provided")
}data class AppTheme(val primaryColor: Color,val textColor: Color,val fontSize: Float
)@Composable
fun ThemeProvider(theme: AppTheme = AppTheme(primaryColor = Color.Blue,textColor = Color.White,fontSize = 16f),content: @Composable () -> Unit
) {CompositionLocalProvider(LocalAppTheme provides theme) {content()}
}@Composable
fun ThemedText(text: String) {val theme = LocalAppTheme.currentText(text = text,color = theme.textColor,fontSize =18.sp)
}@Composable
fun CompositionLocalExample() {ThemeProvider {Box(modifier = Modifier.fillMaxSize().background(LocalAppTheme.current.primaryColor),contentAlignment = Alignment.Center) {ThemedText("Hello CompositionLocal!")}}
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifierdata class PermissionState(val cameraGranted: Boolean,val locationGranted: Boolean,val storageGranted: Boolean,val requestCamera: () -> Unit,val requestLocation: () -> Unit,val requestStorage: () -> Unit
)private val LocalPermissions = compositionLocalOf<PermissionState> {error("No PermissionState provided. Make sure to wrap your content with PermissionProvider.")
}@Composable
fun PermissionProvider(content: @Composable () -> Unit
) {var cameraGranted by remember { mutableStateOf(false) }var locationGranted by remember { mutableStateOf(false) }var storageGranted by remember { mutableStateOf(false) }val permissionState = PermissionState(cameraGranted = cameraGranted,locationGranted = locationGranted,storageGranted = storageGranted,requestCamera = { cameraGranted = true }, // 實際會調用真實的權限請求API,只是演示requestLocation = { locationGranted = true },requestStorage = { storageGranted = true })CompositionLocalProvider(LocalPermissions provides permissionState) {content()}
}@Composable
fun CameraFeature() {val permissions = LocalPermissions.currentColumn(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Text("相機功能")if (permissions.cameraGranted) {Text("? 相機權限已授予")Button(onClick = { /* 使用相機的邏輯 */ }) {Text("拍照")}} else {Text("? 相機權限未授予")Button(onClick = permissions.requestCamera) {Text("請求相機權限")}}}
}@Composable
fun LocationFeature() {val permissions = LocalPermissions.currentColumn(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Text("位置功能")if (permissions.locationGranted) {Text("? 位置權限已授予")Button(onClick = { /* 使用位置的邏輯 */ }) {Text("獲取當前位置")}} else {Text("? 位置權限未授予")Button(onClick = permissions.requestLocation) {Text("請求位置權限")}}}
}@Composable
fun FeaturesScreen() {Column(modifier = Modifier.fillMaxSize(),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.SpaceEvenly) {Text("應用功能", style = androidx.compose.material3.MaterialTheme.typography.headlineMedium)CameraFeature()LocationFeature()}
}@Composable
fun PermissionTest() { PermissionProvider {FeaturesScreen()}
}
注意
避免濫用:CompositionLocal適合共享“全局/半全局”數據,若數據僅在少數幾層傳遞,直接用參數傳遞更清晰。
明確作用域:通過 CompositionLocalProvider限制數據的生效范圍,避免全局污染。
性能考量:CompositionLocal的值變化會導致依賴它的所有組件重組,不宜用于頻繁變化的數據。