使用CompositionLocal简化组合式函数参数

使用CompositionLocal简化组合式函数参数 (tommwq.work)

========================================================================================================================================================================================================================

目录

1. 组合式函数难以维护状态

和对象相比,组合式函数维护状态的能力比较弱。如果所有状态都通过参数列表显示传递,函数将难以维护。每次改动,调用树上所有的函数全部需要改动。如果使用一个全局对象包含所有的状态,一方面引入了全局依赖,一方面对象会变得非常复杂,难以维护一致性。为了解决这个问题,Jetpack通过CompositionLocal提供了一个带有层级结构的状态树,可以理解为OOP中把继承树中所有对象的行为移除之后的结果。

2. 通过CompositionLocal对象隐式传递状态

代码1 定义和使用CompositionLocal

kotlin 复制代码
import androidx.compose.runtime.compositionLocalOf

val ActiveUser = compositionLocalOf<User> { User("someone") }

@Composable
fun SomeScreen() {
    Text(ActiveUser.current.name) // 通过CompositionLocal的成员current访问具体对象。
}

代码2 提供/更换CompositionLocal

kotlin 复制代码
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.CompositionLocalProvider


@Composable
fun App() {

    Screen() // show default user

    val anotherUser by remember { mutableStateOf(User("another")) }
    CompositionLocalProvider(ActiveUser provides anotherUser) {
        Screen() // show another user
    }

    Screen() // show default user again
}

从上面的例子可以看到,通过CompositionLocal可以隐式传递对象,并且传递给组合式函数对象可以由父函数控制,限制在特定范围内。因为我们可以把哪些与组合式函数自身逻辑无关,同时又会影响函数结果的对象放到CompositionLocal中,简化函数参数列表。

假设组合式函数Screen依赖于某个特性Feature,我们可以通过CompositionLocal隐含的传递Feature对象。

kotlin 复制代码
val LocalFeatureNullable = compositionLocalOf<Feature?> { null }
val LocalFeatureMustProvide = compositionLocalOf<Feature> { error("No Feature provided.") }

@Composable
fun Screen() {
    LocalFeatureNullable.current?.let { DisplayWithFeature() } :? DisplayWithoutFeature() 

    Text(LocalFeatureMustProvide.current.name)
}

3. 重组

有两个函数可以创建CompositionLocal对象:

compositionLocalOf

  1. 用于值频繁变化的场景。
  2. 重组时只有依赖该值的代码重组。

staticCompositionLocalOf

  1. 用于值极少变化的场景。
  2. 重组时CompositionLocalProvider范围内的代码都参与重组。

4. 示例

代码3 使用CompositionLocal简化导航

kotlin 复制代码
val LocalNavController = compositionLocalOf<NavHostController> { error("No NavController provided.") }


@Composable
fun Screen1() {
    Text(text = "Screen1")

    val navctl = LocalNavController.current

    Button(
        onClick = {
            navctl.navigate("screen2")
        },
        modifier = Modifier.wrapContentSize()
    ) {
        Text("打开屏幕2")
    }
}

@Composable
fun Screen2() {
    Text(text = "Screen2")

    val navctl = LocalNavController.current

    Button(
        onClick = {
            navctl.navigate("screen1")
        },
        modifier = Modifier.wrapContentSize()
    ) {
        Text("打开屏幕1")
    }
}

@Composable
fun MainScreen() {
    CompositionLocalProvider(LocalNavController provides rememberNavController()) {
        NavHost(LocalNavController.current, startDestination = "screen1") {
            composable("screen1") { Screen1() }
            composable("screen2") { Screen2() }
        }
    }
}

代码4 部分内置的CompositionLocal对象。文件:AndroidCompositionLocals.android.kt

scala 复制代码
package androidx.compose.ui.platform

...

/**
 * The Android [Configuration]. The [Configuration] is useful for determining how to organize the
 * UI.
 */
val LocalConfiguration = compositionLocalOf<Configuration> {
    noLocalProvidedFor("LocalConfiguration")
}

/**
 * Provides a [Context] that can be used by Android applications.
 */
val LocalContext = staticCompositionLocalOf<Context> {
    noLocalProvidedFor("LocalContext")
}

internal val LocalImageVectorCache = staticCompositionLocalOf<ImageVectorCache> {
    noLocalProvidedFor("LocalImageVectorCache")
}

/**
 * The CompositionLocal containing the current [LifecycleOwner].
 */
val LocalLifecycleOwner = staticCompositionLocalOf<LifecycleOwner> {
    noLocalProvidedFor("LocalLifecycleOwner")
}

/**
 * The CompositionLocal containing the current [SavedStateRegistryOwner].
 */
val LocalSavedStateRegistryOwner = staticCompositionLocalOf<SavedStateRegistryOwner> {
    noLocalProvidedFor("LocalSavedStateRegistryOwner")
}

/**
 * The CompositionLocal containing the current Compose [View].
 */
val LocalView = staticCompositionLocalOf<View> {
    noLocalProvidedFor("LocalView")
}

...

代码5 MaterialTheme使用CompositionLocal控制组件样式

less 复制代码
@Composable
fun MaterialTheme(
    colors: Colors = MaterialTheme.colors,
    typography: Typography = MaterialTheme.typography,
    shapes: Shapes = MaterialTheme.shapes,
    content: @Composable () -> Unit
) {
    ...
    CompositionLocalProvider(
        LocalColors provides rememberedColors,
        LocalContentAlpha provides ContentAlpha.high,
        LocalIndication provides rippleIndication,
        LocalRippleTheme provides MaterialRippleTheme,
        LocalShapes provides shapes,
        LocalTextSelectionColors provides selectionColors,
        LocalTypography provides typography
    ) {
        ProvideTextStyle(value = typography.body1) {
            PlatformMaterialTheme(content)
        }
    }
}

5. 参考资料

  1. juejin.cn/post/709789...
相关推荐
黄林晴15 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我16 小时前
flutter 之真手势冲突处理
android·flutter
法的空间16 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止16 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭16 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
jctech16 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户20187928316716 小时前
为何Handler的postDelayed不适合精准定时任务?
android
叽哥17 小时前
Kotlin学习第 8 课:Kotlin 进阶特性:简化代码与提升效率
android·java·kotlin
Cui晨17 小时前
Android RecyclerView展示List<View> Adapter的数据源使用View
android
氦客17 小时前
Android Doze低电耗休眠模式 与 WorkManager
android·suspend·休眠模式·workmanager·doze·低功耗模式·state_doze