Jetpack Compose는 디지털 인터페이스를 만들기 위한 포괄적인 디자인 시스템인 Material Design 구현을 제공합니다. Material Design 구성요소 (버튼, 카드, 스위치 등)는 제품 브랜드를 효과적으로 반영하도록 Material Design을 맞춤설정하는 체계적인 방법인 Material Theming을 기반으로 빌드됩니다. Material 테마에는 색상, 서체, 도형 속성이 포함되어 있습니다. 이러한 속성을 맞춤설정하면 앱을 빌드하는 데 사용되는 구성요소에 변경사항이 자동으로 반영됩니다.
Jetpack Compose는 MaterialTheme 컴포저블을 사용하여 이러한 개념을 구현합니다.
MaterialTheme( colors = // ... typography = // ... shapes = // ... ) { // app content }
MaterialTheme에 전달하는 매개변수를 구성하여 애플리케이션의 테마를 설정합니다.

색상
색상은 Compose에서 데이터 보유 클래스인 Color 클래스로 모델링됩니다.
val Red = Color(0xffff0000) val Blue = Color(red = 0f, green = 0f, blue = 1f)
원하는 대로 색상을 구성할 수 있지만(예: 싱글톤 내에서 또는 정의된 인라인으로 최상위 상수로 구성) 테마에 색상을 지정하고 거기에서 색상을 검색하는 것이 좋습니다. 이 접근 방식을 사용하면 어두운 테마와 중첩된 테마를 지원할 수 있습니다.
Compose는 Colors 클래스를 제공하여 Material 색상 시스템을 모델링합니다. Colors는 밝은 색상 또는 어두운 색상 세트를 만드는 빌더 함수를 제공합니다.
private val Yellow200 = Color(0xffffeb46) private val Blue200 = Color(0xff91a4fc) // ... private val DarkColors = darkColors( primary = Yellow200, secondary = Blue200, // ... ) private val LightColors = lightColors( primary = Yellow500, primaryVariant = Yellow400, secondary = Blue700, // ... )
Colors를 정의한 후 MaterialTheme에 전달할 수 있습니다.
MaterialTheme( colors = if (darkTheme) DarkColors else LightColors ) { // app content }
테마 색상 사용
MaterialTheme.colors를 사용하여 MaterialTheme 컴포저블에 제공된 Colors를 검색할 수 있습니다.
Text( text = "Hello theming", color = MaterialTheme.colors.primary )
표시 경로 및 콘텐츠 색상
많은 구성요소가 한 쌍의 색상 및 콘텐츠 색상을 허용합니다.
Surface( color = MaterialTheme.colors.surface, contentColor = contentColorFor(color), // ... ) { /* ... */ } TopAppBar( backgroundColor = MaterialTheme.colors.primarySurface, contentColor = contentColorFor(backgroundColor), // ... ) { /* ... */ }
이를 통해 컴포저블의 색상을 설정할 수 있을 뿐만 아니라 콘텐츠(그 안에 있는 컴포저블)의 기본 색상을 제공할 수도 있습니다. 많은 컴포저블은 기본적으로 이 콘텐츠 색상을 사용합니다. 예를 들어 Text는 상위 요소의 콘텐츠 색상을 기반으로 색상을 설정하고 Icon은 이 색상을 사용하여 색조를 설정합니다.

contentColorFor() 메서드는 테마 색상에 적절한 'on' 색상을 가져옵니다. 예를 들어 Surface에서 primary 배경색을 설정하면 이 함수를 사용하여 onPrimary를 콘텐츠 색상으로 설정합니다.
테마가 아닌 배경 색상을 설정하는 경우 적절한 콘텐츠 색상도 지정해야 합니다. LocalContentColor를 사용하여 계층 구조의 특정 위치에서 현재 배경에 선호되는 콘텐츠 색상을 가져옵니다.
콘텐츠 알파
콘텐츠를 강조하는 정도를 달리하여 중요도를 전달하고 시각적 계층 구조를 알려야 할 때가 많습니다. Material Design 텍스트 가독성 권장사항에서는 불투명도를 다르게 하여 중요도를 구분하도록 권장합니다.
Jetpack Compose에서는 LocalContentAlpha를 사용해 이를 구현합니다. CompositionLocal 값을 제공하여 계층 구조의 콘텐츠 알파를 지정할 수 있습니다. 중첩된 컴포저블은 이 값을 사용하여 콘텐츠에 알파 처리를 적용할 수 있습니다. 예를 들어 Text 및 Icon은 기본적으로 LocalContentAlpha를 사용하도록 조정된 LocalContentColor 조합을 사용합니다. Material에서는 ContentAlpha 객체에 의해 모델링된 일부 표준 알파 값(high, medium, disabled)을 지정합니다.
// By default, both Icon & Text use the combination of LocalContentColor & // LocalContentAlpha. De-emphasize content by setting content alpha CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) { Text( // ... ) } CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) { Icon( // ... ) Text( // ... ) }
CompositionLocal에 관한 자세한 내용은 CompositionLocal을 사용해 로컬로 범위가 지정되는 데이터를 참고하세요.

ContentAlpha.high를 사용합니다. 두 번째 줄에는 덜 중요한 메타데이터가 포함되어 있으므로 ContentAlpha.medium를 사용합니다.어두운 테마
Compose에서 MaterialTheme 컴포저블에 다양한 Colors 세트를 제공하여 밝은 테마 및 어두운 테마를 구현합니다.
@Composable fun MyTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { MaterialTheme( colors = if (darkTheme) DarkColors else LightColors, /*...*/ content = content ) }
이 예에서 MaterialTheme은 어두운 테마를 사용할지 여부를 지정하는 매개변수를 받는 자체의 구성 가능한 함수로 래핑되어 있습니다. 이 경우 함수는 기기 테마 설정을 쿼리하여 darkTheme의 기본값을 가져옵니다.
다음과 같은 코드를 사용하여 현재 Colors가 밝은지 아니면 어두운지 확인할 수 있습니다.
val isLightTheme = MaterialTheme.colors.isLight Icon( painterResource( id = if (isLightTheme) { R.drawable.ic_sun_24 } else { R.drawable.ic_moon_24 } ), contentDescription = "Theme" )
고도 오버레이
Material에서 고도가 높은 어두운 테마의 표면은 고도 오버레이를 수신하여 배경을 밝게 합니다. 표면의 고도가 높을수록 (암시된 광원에 더 가까워짐) 표면이 더 밝아집니다.
Surface 컴포저블은 어두운 색상을 사용할 때 이러한 오버레이를 자동으로 적용하며, 표시 경로를 사용하는 다른 Material 컴포저블도 마찬가지입니다.
Surface( elevation = 2.dp, color = MaterialTheme.colors.surface, // color will be adjusted for elevation /*...*/ ) { /*...*/ }

surface 색상을 배경으로 사용합니다. 카드와 하단 탐색 메뉴는 배경보다 고도 수준이 다르기 때문에 색상이 약간 다릅니다. 카드는 배경보다 밝고 하단 탐색 메뉴는 카드보다 밝습니다.Surface가 포함되지 않은 맞춤 시나리오의 경우 Surface 구성요소에서 사용하는 ElevationOverlay가 포함된 CompositionLocal인 LocalElevationOverlay를 사용합니다.
// Elevation overlays // Implemented in Surface (and any components that use it) val color = MaterialTheme.colors.surface val elevation = 4.dp val overlaidColor = LocalElevationOverlay.current?.apply( color, elevation )
고도 오버레이를 사용 중지하려면 컴포저블 계층 구조의 선택한 지점에 null를 제공합니다.
MyTheme { CompositionLocalProvider(LocalElevationOverlay provides null) { // Content without elevation overlays } }
제한된 강조 색상
Material에서는 대부분의 경우 primary 색상보다 surface 색상을 사용하여 어두운 테마에 제한된 강조 색상을 적용하는 것이 좋습니다. TopAppBar 및 BottomNavigation와 같은 Material 컴포저블은 기본적으로 이 동작을 구현합니다.

커스텀 시나리오의 경우 primarySurface 확장 프로그램 속성을 사용합니다.
Surface( // Switches between primary in light theme and surface in dark theme color = MaterialTheme.colors.primarySurface, /*...*/ ) { /*...*/ }
서체
Material은 서체 시스템을 정의하여 의미론적으로 이름이 지정된 소수의 스타일을 사용하도록 권장합니다.

Compose는 Typography, TextStyle, 글꼴 관련 클래스를 사용하여 서체 시스템을 구현합니다. Typography 생성자는 각 스타일의 기본값을 제공하므로 맞춤설정하지 않으려는 스타일은 생략할 수 있습니다.
val raleway = FontFamily( Font(R.font.raleway_regular), Font(R.font.raleway_medium, FontWeight.W500), Font(R.font.raleway_semibold, FontWeight.SemiBold) ) val myTypography = Typography( h1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W300, fontSize = 96.sp ), body1 = TextStyle( fontFamily = raleway, fontWeight = FontWeight.W600, fontSize = 16.sp ) /*...*/ ) MaterialTheme(typography = myTypography, /*...*/) { /*...*/ }
전체에서 동일한 서체를 사용하려면 defaultFontFamily 매개변수를 지정하고 TextStyle 요소의 fontFamily를 생략합니다.
val typography = Typography(defaultFontFamily = raleway) MaterialTheme(typography = typography, /*...*/) { /*...*/ }
텍스트 스타일 사용
TextStyle 요소는 MaterialTheme.typography를 사용하여 액세스합니다. 다음과 같이 TextStyle 요소를 가져옵니다.
Text( text = "Subtitle2 styled", style = MaterialTheme.typography.subtitle2 )

도형
Material은 도형 시스템을 정의합니다. 이 시스템을 통해 대형, 중형, 소형 구성요소의 도형을 정의할 수 있습니다.

Compose는 Shapes 클래스로 도형 시스템을 구현합니다. 이 클래스를 사용하면 각 크기 카테고리에 CornerBasedShape를 지정할 수 있습니다.
val shapes = Shapes( small = RoundedCornerShape(percent = 50), medium = RoundedCornerShape(0f), large = CutCornerShape( topStart = 16.dp, topEnd = 0.dp, bottomEnd = 0.dp, bottomStart = 16.dp ) ) MaterialTheme(shapes = shapes, /*...*/) { /*...*/ }
많은 구성요소가 기본적으로 이러한 도형을 사용합니다. 예를 들어 Button, TextField, FloatingActionButton는 기본적으로 작고, AlertDialog는 기본적으로 중간이며, ModalDrawer는 기본적으로 큽니다. 전체 매핑은 도형 스키마 참조를 참고하세요.
도형 사용
Shape 요소는 MaterialTheme.shapes를 사용하여 액세스합니다. 다음과 같은 코드를 사용하여 Shape 요소를 가져옵니다.
Surface( shape = MaterialTheme.shapes.medium, /*...*/ ) { /*...*/ }
기본 스타일
Compose에는 Android 뷰의 기본 스타일과 동일한 개념이 없습니다. Material 구성요소를 래핑하는 자체 overload 구성 가능한 함수를 만들어 유사한 기능을 제공할 수 있습니다. 예를 들어 버튼 스타일을 만들려면 자체 구성 가능한 함수로 버튼을 래핑하고 변경하려는 매개변수 또는 변경해야 하는 매개변수를 직접 설정하며 포함하는 컴포저블에 매개변수로 다른 매개변수를 노출합니다.
@Composable fun MyButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary ), onClick = onClick, modifier = modifier, content = content ) }
테마 오버레이
MaterialTheme 컴포저블을 중첩하여 Compose의 Android 뷰에서 테마 오버레이와 동일한 결과를 얻을 수 있습니다. MaterialTheme는 색상, 서체, 도형을 현재 테마 값으로 기본 설정하므로 테마가 이러한 매개변수 중 하나만 설정하는 경우 다른 모든 매개변수는 기본값을 유지합니다.
또한 뷰 기반 화면을 Compose로 이전할 때 android:theme 속성을 사용하는 것에 유의하세요. Compose UI 트리의 관련 부분에 새로운 MaterialTheme이 필요할 수 있습니다.
이 예에서 세부정보 화면은 화면 대부분에 PinkTheme를 사용하고 관련 섹션에는 BlueTheme를 사용합니다. 다음 스크린샷과 코드는 이 개념을 보여줍니다.

@Composable fun DetailsScreen(/* ... */) { PinkTheme { // other content RelatedSection() } } @Composable fun RelatedSection(/* ... */) { BlueTheme { // content } }
구성요소 상태
상호작용 (클릭, 전환 등)할 수 있는 Material 구성요소는 다양한 시각적 상태일 수 있습니다. 상태에는 사용 설정, 중지, 누름 상태 등이 있습니다.
종종 컴포저블에는 enabled 매개변수가 있습니다. 이 매개변수를 false로 설정하면 상호작용이 방지되고, 색상과 고도 같이 구성요소 상태를 시각적으로 전달하기 위한 속성이 변경됩니다.

enabled = true (왼쪽) 및 enabled = false (오른쪽) 버튼대부분의 경우 색상과 고도 같은 값에는 기본값을 사용할 수 있습니다. 다양한 상태에서 사용되는 값을 구성해야 하는 경우 사용할 수 있는 클래스와 편의 함수가 있습니다. 다음 버튼 예시를 참고하세요.
Button( onClick = { /* ... */ }, enabled = true, // Custom colors for different states colors = ButtonDefaults.buttonColors( backgroundColor = MaterialTheme.colors.secondary, disabledBackgroundColor = MaterialTheme.colors.onBackground .copy(alpha = 0.2f) .compositeOver(MaterialTheme.colors.background) // Also contentColor and disabledContentColor ), // Custom elevation for different states elevation = ButtonDefaults.elevation( defaultElevation = 8.dp, disabledElevation = 2.dp, // Also pressedElevation ) ) { /* ... */ }
enabled = true (왼쪽) 및 enabled = false (오른쪽)이 있는 버튼, 조정된 색상 및 고도 값물결
Material Components는 물결 효과를 사용하여 구성요소가 상호작용 중임을 나타냅니다. 계층 구조에서 MaterialTheme를 사용하는 경우 Ripple는 clickable 및 indication와 같은 수정자 내에서 기본 Indication로 사용됩니다.
대부분의 경우 기본 Ripple을 사용할 수 있습니다. 모양을 구성해야 하는 경우 RippleTheme를 사용하여 색상 및 알파와 같은 속성을 변경할 수 있습니다.
RippleTheme를 확장하고 defaultRippleColor 및 defaultRippleAlpha 유틸리티 함수를 사용할 수 있습니다. 그런 다음 LocalRippleTheme를 사용하여 계층 구조에 맞춤 물결 효과 테마를 제공할 수 있습니다.
@Composable fun MyApp() { MaterialTheme { CompositionLocalProvider( LocalRippleTheme provides SecondaryRippleTheme ) { // App content } } } @Immutable private object SecondaryRippleTheme : RippleTheme { @Composable override fun defaultColor() = RippleTheme.defaultRippleColor( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) @Composable override fun rippleAlpha() = RippleTheme.defaultRippleAlpha( contentColor = MaterialTheme.colors.secondary, lightTheme = MaterialTheme.colors.isLight ) }
RippleConfiguration을 사용하여 제공된 서로 다른 물결 효과 값의 버튼자세히 알아보기
Compose의 머티리얼 테마 설정에 관한 자세한 내용은 다음 추가 리소스를 참고하세요.
Codelabs
동영상
추천 서비스
- 참고: JavaScript가 사용 중지되어 있으면 링크 텍스트가 표시됩니다.
- Compose의 맞춤 디자인 시스템
- Compose의 Material 2에서 Material 3으로 이전
- Compose의 접근성