Compose'da özel tasarım sistemleri

Önerdiğimiz tasarım sistemimiz Material, jetpack Compose'u da Malzeme kullanmaya devam etmek zorunda kalmazsınız. Malzeme üretildi Bu yüzden, kendi tasarım sisteminizi yalnızca herkese açık API'lerde aynı şekilde yürütülür.

Uygulayabileceğiniz çeşitli yaklaşımlar vardır:

Özel bir tasarım sistemiyle Material bileşenlerini kullanmaya devam etmek isteyebilirsiniz. Bunu yapmak mümkündür ancak benimsediğiniz yaklaşıma uygun olarak dikkat etmeniz gereken bazı noktalar vardır.

MaterialTheme tarafından kullanılan alt düzey yapılar ve API'ler hakkında daha fazla bilgi edinmek için ve özel tasarım sistemleri için Oluşturma'da temanın anatomisi rehberine göz atın.

Materyal temasını genişletme

Compose Material, Material yönergelerine uymayı basit ve tür açısından güvenli hale getirmek için Material tema oluşturma özelliğini yakından modeller. Ancak, renk, yazı ve şekil setlerini daha fazla ek bileşenle değerler.

En basit yaklaşım, uzantı özellikleri eklemektir:

// Use with MaterialTheme.colorScheme.snackbarAction
val ColorScheme.snackbarAction: Color
    @Composable
    get() = if (isSystemInDarkTheme()) Red300 else Red700

// Use with MaterialTheme.typography.textFieldInput
val Typography.textFieldInput: TextStyle
    get() = TextStyle(/* ... */)

// Use with MaterialTheme.shapes.card
val Shapes.card: Shape
    get() = RoundedCornerShape(size = 20.dp)

Bu, MaterialTheme kullanım API'leriyle tutarlılık sağlar. Bu duruma bir örnek Compose'un kendisi tarafından tanımlanan surfaceColorAtElevation bu değer, yüksekliğe bağlı olarak kullanılması gereken yüzey rengini belirler.

Diğer bir yaklaşım, MaterialTheme ve değerlerini "sarmalayan" genişletilmiş bir tema tanımlamaktır.

Biri caution ve onCaution olmak üzere iki renk daha eklemek istediğinizi varsayalım yarı tehlikeli eylemler için kullanılan sarı renk, yalnızca mevcut Malzeme renkleri:

@Immutable
data class ExtendedColors(
    val caution: Color,
    val onCaution: Color
)

val LocalExtendedColors = staticCompositionLocalOf {
    ExtendedColors(
        caution = Color.Unspecified,
        onCaution = Color.Unspecified
    )
}

@Composable
fun ExtendedTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val extendedColors = ExtendedColors(
        caution = Color(0xFFFFCC02),
        onCaution = Color(0xFF2C2D30)
    )
    CompositionLocalProvider(LocalExtendedColors provides extendedColors) {
        MaterialTheme(
            /* colors = ..., typography = ..., shapes = ... */
            content = content
        )
    }
}

// Use with eg. ExtendedTheme.colors.caution
object ExtendedTheme {
    val colors: ExtendedColors
        @Composable
        get() = LocalExtendedColors.current
}

Bu, MaterialTheme kullanım API'lerine benzer. Ayrıca birden fazla temayı destekler. ExtendedTheme öğelerini MaterialTheme ile aynı şekilde iç içe yerleştirebilirsiniz.

Material bileşenlerini kullanma

Materyal Teması genişletilirken mevcut MaterialTheme değerleri korunur Malzeme bileşenlerinin varsayılan değerleri hâlâ makul.

Bileşenlerde genişletilmiş değerler kullanmak istiyorsanız bunları kendi birleştirilebilir işlevlerinize sarmalayın. Değiştirmek istediğiniz değerleri doğrudan ayarlayın ve diğer değerleri içeren birleştirilebilir işleve parametre olarak gösterin:

@Composable
fun ExtendedButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            containerColor = ExtendedTheme.colors.caution,
            contentColor = ExtendedTheme.colors.onCaution
            /* Other colors use values from MaterialTheme */
        ),
        onClick = onClick,
        modifier = modifier,
        content = content
    )
}

Daha sonra, Button kullanımlarını ExtendedButton ile değiştirirsiniz; burada: uygun olmalıdır.

@Composable
fun ExtendedApp() {
    ExtendedTheme {
        /*...*/
        ExtendedButton(onClick = { /* ... */ }) {
            /* ... */
        }
    }
}

Malzeme alt sistemlerini değiştirme

Materyal Temaları oluşturmak yerine, örneğin bir veya daha fazla sistemleri (Colors, Typography veya Shapes) özel bir uygulamayla, diğerlerini korur.

Renk değerlerini korurken tür ve şekil sistemlerini değiştirmek istediğinizi varsayalım. sistem:

@Immutable
data class ReplacementTypography(
    val body: TextStyle,
    val title: TextStyle
)

@Immutable
data class ReplacementShapes(
    val component: Shape,
    val surface: Shape
)

val LocalReplacementTypography = staticCompositionLocalOf {
    ReplacementTypography(
        body = TextStyle.Default,
        title = TextStyle.Default
    )
}
val LocalReplacementShapes = staticCompositionLocalOf {
    ReplacementShapes(
        component = RoundedCornerShape(ZeroCornerSize),
        surface = RoundedCornerShape(ZeroCornerSize)
    )
}

@Composable
fun ReplacementTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val replacementTypography = ReplacementTypography(
        body = TextStyle(fontSize = 16.sp),
        title = TextStyle(fontSize = 32.sp)
    )
    val replacementShapes = ReplacementShapes(
        component = RoundedCornerShape(percent = 50),
        surface = RoundedCornerShape(size = 40.dp)
    )
    CompositionLocalProvider(
        LocalReplacementTypography provides replacementTypography,
        LocalReplacementShapes provides replacementShapes
    ) {
        MaterialTheme(
            /* colors = ... */
            content = content
        )
    }
}

// Use with eg. ReplacementTheme.typography.body
object ReplacementTheme {
    val typography: ReplacementTypography
        @Composable
        get() = LocalReplacementTypography.current
    val shapes: ReplacementShapes
        @Composable
        get() = LocalReplacementShapes.current
}

Material bileşenlerini kullanma

Bir veya daha fazla MaterialTheme sistemi değiştirildiğinde, malzeme bileşenlerinin olduğu gibi kullanılması istenmeyen malzeme rengi, türü veya şekli değerlerine neden olabilir.

Bileşenlerde değiştirme değerleri kullanmak istiyorsanız bunları kendi birleştirilebilir işlevlerinize sarmalayın, ilgili sistem için değerleri doğrudan ayarlayın ve diğerlerini kapsayıcı birleştirilebilir işleve parametre olarak gösterin.

@Composable
fun ReplacementButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        shape = ReplacementTheme.shapes.component,
        onClick = onClick,
        modifier = modifier,
        content = {
            ProvideTextStyle(
                value = ReplacementTheme.typography.body
            ) {
                content()
            }
        }
    )
}

Ardından, uygun durumlarda Button kullanımlarını ReplacementButton ile değiştirirsiniz.

@Composable
fun ReplacementApp() {
    ReplacementTheme {
        /*...*/
        ReplacementButton(onClick = { /* ... */ }) {
            /* ... */
        }
    }
}

Tamamen özel bir tasarım sistemi uygulama

Materyal Temaları'nı tamamen özel bir tasarım sistemiyle değiştirmek isteyebilirsiniz. MaterialTheme'nin aşağıdaki sistemleri sağladığını varsayalım:

  • Colors, Typography ve Shapes: Materyal Teması Oluşturma sistemleri
  • TextSelectionColors: Text ve TextField tarafından metin seçimi için kullanılan renkler
  • Ripple ve RippleTheme: Indication öğesinin materyal uygulaması

Materyal bileşenleri kullanmaya devam etmek istiyorsanız bazı bileşenleri özel temalarınızda veya temalarınızda kullanabilir ya da bileşenlerine ayırmanızı sağlar.

Ne var ki, tasarım sistemleri Materyal’in dayandığı kavramlarla sınırlı değildir. Diğer kavramları temalarla uyumlu hale getirmek için mevcut sistemleri değiştirebilir ve yeni sınıflar ve türlerle tamamen yeni sistemler kullanabilirsiniz.

Aşağıdaki kodda, degradeler (List<Color>) içeren özel bir renk sistemi modelliyoruz, bir tür sistemi ekliyoruz, yeni bir yükseklik sistemi tanıtıyoruz ve MaterialTheme tarafından sağlanan diğer sistemleri hariç tutuyoruz:

@Immutable
data class CustomColors(
    val content: Color,
    val component: Color,
    val background: List<Color>
)

@Immutable
data class CustomTypography(
    val body: TextStyle,
    val title: TextStyle
)

@Immutable
data class CustomElevation(
    val default: Dp,
    val pressed: Dp
)

val LocalCustomColors = staticCompositionLocalOf {
    CustomColors(
        content = Color.Unspecified,
        component = Color.Unspecified,
        background = emptyList()
    )
}
val LocalCustomTypography = staticCompositionLocalOf {
    CustomTypography(
        body = TextStyle.Default,
        title = TextStyle.Default
    )
}
val LocalCustomElevation = staticCompositionLocalOf {
    CustomElevation(
        default = Dp.Unspecified,
        pressed = Dp.Unspecified
    )
}

@Composable
fun CustomTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val customColors = CustomColors(
        content = Color(0xFFDD0D3C),
        component = Color(0xFFC20029),
        background = listOf(Color.White, Color(0xFFF8BBD0))
    )
    val customTypography = CustomTypography(
        body = TextStyle(fontSize = 16.sp),
        title = TextStyle(fontSize = 32.sp)
    )
    val customElevation = CustomElevation(
        default = 4.dp,
        pressed = 8.dp
    )
    CompositionLocalProvider(
        LocalCustomColors provides customColors,
        LocalCustomTypography provides customTypography,
        LocalCustomElevation provides customElevation,
        content = content
    )
}

// Use with eg. CustomTheme.elevation.small
object CustomTheme {
    val colors: CustomColors
        @Composable
        get() = LocalCustomColors.current
    val typography: CustomTypography
        @Composable
        get() = LocalCustomTypography.current
    val elevation: CustomElevation
        @Composable
        get() = LocalCustomElevation.current
}

Material bileşenlerini kullanma

MaterialTheme mevcut olmadığında, Materyal bileşenleri olduğu gibi kullanıldığında istenmeyen Malzeme rengi, türü, şekil değerleri ve gösterge davranışında hata meydana gelebilir.

Bileşenlerde özel değerler kullanmak istiyorsanız bunları kendi birleştirilebilir işlevlerinize sarmalayın, ilgili sistem için değerleri doğrudan ayarlayın ve diğerlerini içeren birleştirilebilir işleve parametre olarak gösterin.

Özel temanızda belirlediğiniz değerlere erişmenizi öneririz. Alternatif olarak, temanızda Color, TextStyle, Shape veya bu sistemleri sabit olarak kodlayabilirsiniz.

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            containerColor = CustomTheme.colors.component,
            contentColor = CustomTheme.colors.content,
            disabledContainerColor = CustomTheme.colors.content
                .copy(alpha = 0.12f)
                .compositeOver(CustomTheme.colors.component),
            disabledContentColor = CustomTheme.colors.content
                .copy(alpha = 0.38f)

        ),
        shape = ButtonShape,
        elevation = ButtonDefaults.elevatedButtonElevation(
            defaultElevation = CustomTheme.elevation.default,
            pressedElevation = CustomTheme.elevation.pressed
            /* disabledElevation = 0.dp */
        ),
        onClick = onClick,
        modifier = modifier,
        content = {
            ProvideTextStyle(
                value = CustomTheme.typography.body
            ) {
                content()
            }
        }
    )
}

val ButtonShape = RoundedCornerShape(percent = 50)

Eğimlendirmeleri temsil etmek için List<Color> gibi yeni sınıf türleri tanıttıysanız bileşenleri sarmalamak yerine sıfırdan uygulamak daha iyi olabilir. Örnek olarak, JetsnackButton 10 kat daha fazla.