본문 바로가기

Compose

[Compose] CompositionLocal

 

CompositionLocal

암시적으로 Composition을 통해 데이터를 전달하는 도구

UI 트리의 특정 범위에서 암시적으로 데이터 전달 가능

 

Compose에서 일반적으로 매개변수로 데이터를 전달한다

하지만 색상이나 텍스트 스타일처럼 자주 사용되는 데이터일 경우, 모든 함수에 매개변수로 전달하는 것은 번거로울 수 있다

이런 경우 CompositionLocal을 사용하면 명시적으로 매개변수로 전달할 필요 없이 데이터를 사용할 수 있다

 

 

동작 방식

 

CompositionLocal 정의

compositionLocalOf나 staticCompositionLocalOf 사용

fun <T> compositionLocalOf(
    policy: SnapshotMutationPolicy<T> =
        structuralEqualityPolicy(),
    defaultFactory: () -> T
): ProvidableCompositionLocal<T> = DynamicProvidableCompositionLocal(policy, defaultFactory)


fun <T> staticCompositionLocalOf(defaultFactory: () -> T): ProvidableCompositionLocal<T> =
    StaticProvidableCompositionLocal(defaultFactory)

 

 

CompositionLocal

@Stable
sealed class CompositionLocal<T> constructor(defaultFactory: () -> T) {
    internal val defaultValueHolder = LazyValueHolder(defaultFactory)

    internal abstract fun updatedStateOf(value: T, previous: State<T>?): State<T>

    // 가장 가까운 CompositionLocalProvider에서 제공된 값
    @OptIn(InternalComposeApi::class)
    inline val current: T
        @ReadOnlyComposable
        @Composable
        get() = currentComposer.consume(this)
}

 

새로운 값을 제공하려면 provides 중위 함수를 사용한다

infix fun provides(value: T) = ProvidedValue(this, value, true)

 

provides 함수는 CompositionLocalProvider와 CompositionLocal 키를 value에 연결

 

CompositionLocalProvider

CompositionLocal의 값을 변경할 범위 지정 (content 람다)

@Composable
@OptIn(InternalComposeApi::class)
fun CompositionLocalProvider(value: ProvidedValue<*>, content: @Composable () -> Unit) {
    currentComposer.startProvider(value)
    content()
    currentComposer.endProvider()
}

 

compositionLocal을 사용하는 예시

// CompositionLocal 정의
val LocalContentColor = compositionLocalOf { Color.Gray }

// content 람다 내에서 current를 사용하면 해당 CompositionLocal의 변경된 값을 가져올 수 있다
@Composable
fun ContentWithLocalColor() {
    val contentColor = LocalContentColor.current // 현재 CompositionLocal 값
    Text("${contentColor.toString()}", color = contentColor)
}

@Composable
fun Main() {
    // 기본값 표시
    ContentWithLocalColor()

    // CompositionLocalProvider를 사용하여 값 변경
    CompositionLocalProvider(LocalContentColor provides Color.Blue) {
        ContentWithLocalColor() // 파란색으로 표시
    }

    // 다른 값으로 변경
    CompositionLocalProvider(LocalContentColor provides Color.Red) {
        ContentWithLocalColor() // 빨간색으로 표시
    }
}