🪧

Jetpack compose 1.9.0 でネオンサイン風の枠線を遊びで作った

に公開

Jetpack compose 1.9.0

Jetpack compose BoM 2025.08.00 (1.9.0) がリリースされました 🎉

https://android-developers.googleblog.com/2025/08/whats-new-in-jetpack-compose-august-25-release.html

  • Shadows
  • New Visibility modifiers
  • Rich styling in OutputTransformation
  • LazyLayout
  • New annotations and Lint checks

など様々な更新がありましたが、今回 Shadows の追加でできるようになったネオンサイン風の枠線を遊びで作りました。

成果物
今回の成果物

Shadows

今回のアップデートで ModifierdropShadowinnerShadow が簡単に設定できるようになりました。

dropShadow

公式ブログに書かれている DropShadow というクラスは見つからなかったので API reference を参照して Shadow を使用しています。
修飾子の順序の重要性 にも記載の通り順番が重要で、background よりも前に記述する必要があります。

@Preview(showBackground = true)
@Composable
private fun DropShadowPreview() {
    val shape = RoundedCornerShape(size = 20.dp)

    Box(contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .padding(all = 20.dp)
                .size(size = 200.dp)
                .dropShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 12.dp,
                        color = Color(0xFF0088ff),
                    ),
                )
                .background(
                    color = Color(0xFF0088ff),
                    shape = shape,
                ),
        )
    }
}

Drop shadow
dropShadow

innerShadow

こちらも同じく公式ブログに書かれている InnerShadow というクラスは見つからなかったので API reference を参照して Shadow を使用しています。
background よりも後に記述する必要があります。

@Preview(showBackground = true)
@Composable
private fun InnerShadowPreview() {
    val shape = RoundedCornerShape(size = 20.dp)

    Box(contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .padding(all = 20.dp)
                .size(size = 200.dp)
                .background(
                    color = Color(0xFF0088ff),
                    shape = shape,
                )
                .innerShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 12.dp,
                        color = Color.Black,
                    ),
                )
        )
    }
}

Inner shadow
innerShadow

ネオンサイン風の枠線

dropShadowinnerShadow を合わせると CSS で見かけるネオンサインのような枠線ができました。
spread を使用するとさらにそれっぽく見えます。

@Preview(showBackground = true, backgroundColor = 0xFF000000)
@Composable
private fun NeonPreview() {
    val neonColor = Color(0xFF0088ff)
    val shape = RoundedCornerShape(size = 20.dp)

    Box(contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .padding(20.dp)
                .size(200.dp, 100.dp)
                .dropShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 12.dp,
                        color = neonColor,
                        spread = 3.dp,
                    ),
                )
                .background(
                    color = Color.Black,
                    shape = shape,
                )
                .border(
                    width = 2.dp,
                    color = Color.White,
                    shape = shape,
                )
                .innerShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 18.dp,
                        color = neonColor,
                        spread = 3.dp,
                    ),
                ),
        )
    }
}

成果物
冒頭の画像と同じ成果物

テキストもネオンにするには?(誤魔化し)

今回の dropShadowinnerShadow はあくまで Box などの Compose に対して設定できるものなので、Text では従来通り Canvas で実装するか、下記のように Shadow (dropShadow に使用しているものとは別のクラス) を使用して誤魔化す方法もあります。
それでも枠線がそれなりにネオンっぽいのでぱっと見、誤魔化せている(いない)。

@Preview(showBackground = true, backgroundColor = 0xFF000000)
@Composable
fun NeonPreview() {
    val neonColor = Color(0xFF0088ff)
    val shape = RoundedCornerShape(size = 20.dp)

    Box(contentAlignment = Alignment.Center) {
        Box(
            modifier = Modifier
                .padding(20.dp)
                .size(200.dp, 100.dp)
                .dropShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 12.dp,
                        color = neonColor,
                        spread = 3.dp,
                    ),
                )
                .background(
                    color = Color.Black,
                    shape = shape,
                )
                .border(
                    width = 2.dp,
                    color = Color.White,
                    shape = shape,
                )
                .innerShadow(
                    shape = shape,
                    shadow = Shadow(
                        radius = 18.dp,
                        color = neonColor,
                        spread = 3.dp,
                    ),
                ),
        ) {
            Text(
                text = "Hello world!",
                style = TextStyle(
                    color = Color.White,
                    fontSize = 24.sp,
                    shadow = androidx.compose.ui.graphics.Shadow(
                        color = neonColor,
                        blurRadius = 20f,
                    ),
                ),
                modifier = Modifier.align(Alignment.Center),
            )
        }
    }
}

テキスト付きネオン
テキスト付きネオン

雑感

今までは Canvas で実装するしかなかったのですが、簡単に実装できる API が置かれると嬉しいですね。
innerPaddingbackground に画像を設定するなどで表現の幅が増える気がします。
プロダクトに活用できるかは別としても様々なデザインを標準の API で実装できることで、さらなる表現が可能になると実装も捗りそうです。

Discussion