[Compose] custom modifier

1 분 소요

custom modifier를 만드는 데는 크게 두 가지 방법이 존재한다.

  1. 기존 modifier를 가져와 chaining하는 방식
  2. low level의 Modifier.Node를 활용해 조금 더 유연하게 custom하는 방식

이번 게시글에서는 1번 방식에 대해 다뤄보고자 한다!! chaining을 활용하는 데에도 크게 두 가지 방법이 존재한다.

  1. 단순 chianing 방식
  2. modifier factory 활용하는 방식

1. 단순한 chaining 방식

fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)
  • 여기서 clip은 graphicsLayer를 사용해 구현되기 때문에 해당 블록으로 감싸 custom을 진행한다.
fun Modifier.myBackground(color: Color) = padding(16.dp)
    .clip(RoundedCornerShape(8.dp))
    .background(color)
  • 자주 쓰이는 modifier function을 그룹으로 묶어 사용이 가능하다!

2. modifier factory 사용한 chaining 방식

@Composable
fun Modifier.fade(enable: Boolean): Modifier {
    val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)
    return this then Modifier.graphicsLayer { this.alpha = alpha }
}

2-1. modifier factory를 통해 composition local 제공

  • 만약 custom modifier를 통해 CompositionLocal의 defaut value를 제공하고자 한다면? 가능하다!!
@Composable
fun Modifier.fadedBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

⛔️ 단, CompositionLocal 값은 call site에 결정된다. 즉, 사용될 때가 아니라 불려질 때 결정되므로 주의해야 한다!

@Composable
fun Modifier.myBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

@Composable
fun MyScreen() {
    CompositionLocalProvider(LocalContentColor provides Color.Green) {
        // Background modifier created with green background
        val backgroundModifier = Modifier.myBackground()

        // LocalContentColor updated to red
        CompositionLocalProvider(LocalContentColor provides Color.Red) {

            // Box will have green background, not red as expected.
            Box(modifier = backgroundModifier)
        }
    }
}

⛔️ composable function modifiers는 절대 스킵되지 않는다?

composable function modifier는 반환값이 있기 때문에 절대 스킵되지 않는다!

이 말은 즉, recomposition이 일어날 때마다 call 되기 때문에 만약 recomposition이 빈번히 일어난다면 많은 비용이 들게 된다.

⛔️ composable function modifier 역시 composable 함수 내에서 호출되어야 한다?

val extractedModifier = Modifier.background(Color.Red) // Hoisted to save allocations

@Composable
fun Modifier.composableModifier(): Modifier {
    val color = LocalContentColor.current.copy(alpha = 0.5f)
    return this then Modifier.background(color)
}

@Composable
fun MyComposable() {
    val composedModifier = Modifier.composableModifier() // Cannot be extracted any higher
}

즉, composable function modifier를 이용하게 된다면 hoisting이 제한된다. 하지만 non-composable modifier factory의 겨우 위 코드의 extractedModifier처럼 composable 밖까지 호이스팅이 가능해진다. 이 점을 참고하자!


📝 참고 자료

댓글남기기