Stillerle tema oluşturma

Stilleri kullanarak uygulamalarınızı geliştirmenin çeşitli yolları vardır. Seçiminiz, uygulamanızın Materyal Tasarım'ı benimseme durumuyla ilgili olarak nerede yer aldığına bağlıdır:

  1. Materyal Tasarım'ın kullanılmadığı, tamamen özel tasarım sistemi
    • Öneri: Temadaki değerleri kullanan bileşen stilleri tanımlayın ve tasarım sistemi bileşenlerinde stil parametrelerini kullanıma sunun.
  2. Materyal Tasarım'ı kullanma
    • Öneri: Stiller ile entegrasyon için Material'ın kullanılmasını bekleyin. Mümkün olduğunda kendi bileşenlerinizde stiller kullanın.

Stil katmanı

Geleneksel oluşturma modelinde özelleştirme genellikle MaterialTheme tarafından sağlanan genel jetonların (renkler ve tipografi) geçersiz kılınmasına veya mümkün olduğunda bir tasarım sistemi composable'ının özelliklerinin sarmalanıp geçersiz kılınmasına dayanır. Bazen, malzeme katmanında alt sistemler veya parametreler aracılığıyla kullanıma sunulmayan ancak bileşenin kendisinde sabit kodlanmış varsayılanlar olan özellikler bulunur.

Styles API ile alt sistemler ve bileşenler arasında köprü görevi gören yeni bir soyutlama katmanı olan Styles'ı kullanabilirsiniz.

Katman Sorumluluk Örnek
Alt sistem değerleri Adlandırılmış değerler val Primary = Color(0xFF34A85E)
Atomic Styles Tam olarak bir özellik değişikliği yapan stil val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic
Bileşen Stilleri Bileşene özgü yapılandırmalar Birincil arka plan ve 16 dp dolguya sahip bir düğme. val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
Bileşenler Stili kullanan işlevsel kullanıcı arayüzü öğesi. Button(style = buttonStyle) { ... }
Yeni katman tanıtımıyla birlikte stillerle temalandırmayı gösteren şema
Şekil 1. Bir bileşen örneği ve temadaki stillere nasıl eriştiği.

Atomik ve monolitik stiller

Styles API ile bir stili ayrı atomik stillere ayırabilirsiniz. baseButtonStyle gibi karmaşık, bileşene özgü stiller tanımlamak yerine küçük, tek amaçlı yardımcı stiller de oluşturabilirsiniz. Bunlar "atomlarınız" olarak kabul edilir.

// Define single-purpose "atomic" styles
val paddingAtomic = Style {
    contentPadding(16.dp)
}
val roundedCornerShapeAtomic = Style {
    shape(RoundedCornerShape(8.dp))
}
val primaryBackgroundAtomic = Style {
    background(Color.Blue)
}
val largeSizeAtomic = Style {
    size(100.dp, 40.dp)
}
val interactiveShadowAtomic = Style {
    hovered {
        animate {
            dropShadow(
                Shadow(
                    offset = DpOffset(
                        0.dp,
                        0.dp
                    ),
                    radius = 2.dp,
                    spread = 0.dp,
                    color = Color.Blue,
                )
            )
        }
    }
}

"Sonra" bağlacının kullanıldığı kompozisyonlar

Yeni Styles API'nin güçlü özelliklerinden biri, birden fazla Style nesneyi birleştirmenize olanak tanıyan then operatörüdür. Bu sayede, atomik yardımcı sınıfları kullanarak bir bileşen oluşturabilirsiniz.

Geleneksel (atomik olmayan):

// One large monolithic style
val buttonStyle = Style {
    contentPadding(16.dp)
    shape(RoundedCornerShape(8.dp))
    background(Color.Blue)
}

Atomik yeniden düzenleme:

// Combine atoms to create the final appearance
val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic

Tasarım sisteminizde stilleri kullanma

Tasarım sisteminizde stilleri kullanırken, tasarım sisteminizin spektrumda bulunduğu yere bağlı olarak aşağıdaki seçenekleri göz önünde bulundurun.

Stillerle özel tasarım sistemi

Ne zaman kullanmalısınız? Material Design'a dayanmayan kapsamlı bir marka kılavuzu aldınız ve Material Design'ı kullanmayı planlamıyorsunuz.

Strateji: Tamamen özel bir tasarım sistemi uygulayın ve stilleri temanın bir parçası olarak kullanıma sunun.

Bu seçenek, ana tasarım sistemi diliniz olarak Material'ı kullanmıyorsanız özel yoldur. Görsel tanımlar için MaterialTheme'ı tamamen atlıyor ve kendi özel temanızı zaten oluşturmuş oluyorsunuz. Stilleriniz için kapsayıcı görevi gören bir CompanyTheme oluşturursunuz.

  • İşleyiş şekli: Sisteminizdeki her bileşen için Style nesnelerini içeren bir CompanyTheme nesnesi oluşturun. Bileşenleriniz (Material mantığı etrafındaki sarmalayıcılar veya özel Box ya da Layout uygulamaları) bu stilleri doğrudan kullanır ve tasarım sisteminizin tüketicileri için bir Style parametresi sunar.
  • Stil katmanı: Stiller, tasarım sisteminizin temel tanımıdır. Jetonlar, bu stillere aktarılan adlandırılmış değişkenlerdir. Bu, durum değişiklikleri için benzersiz animasyonlar tanımlama (örneğin, basıldığında ölçeği ve rengi animasyonla değiştirme) gibi derin özelleştirmelere olanak tanır.

Material'ı kullanmadan kendi özel temanızı oluşturuyorsanız ve stilleri kullanmak istiyorsanız stil listenizi temanıza ekleyin. Bu sayede, temel stillerinize projenizin herhangi bir yerinden erişebilirsiniz.

  1. Uygulamanızdaki çeşitli stilleri depolayan bir Styles sınıfı oluşturun ve varsayılanları oluşturun. Örneğin, Jetsnack uygulamasında sınıfın adı JetsnackStyles:

    object JetsnackStyles{
        val buttonStyle: Style = Style {
            shape(shapes.medium)
            background(colors.brand)
            contentColor(colors.textPrimary)
            contentPaddingVertical(8.dp)
            contentPaddingHorizontal(24.dp)
            textStyle(typography.labelLarge)
            disabled {
                animate {
                    background(colors.brandSecondary)
                }
            }
        }
        val cardStyle: Style = Style {
            shape(shapes.medium)
            background(colors.uiBackground)
            contentColor(colors.textPrimary)
        }
    }

  2. Genel temanızın bir parçası olarak Styles sağlayın ve alt sistemlere erişmek için StyleScope üzerinde yardımcı uzantı işlevlerini kullanıma sunun:

    @Immutable
    class JetsnackTheme(
        val colors: JetsnackColors = LightJetsnackColors,
        val typography: androidx.compose.material3.Typography = androidx.compose.material3.Typography(),
        val shapes: Shapes = Shapes()
    ) {
        companion object {
            val colors: JetsnackColors
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.colors
    
            val typography: androidx.compose.material3.Typography
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.typography
    
            val shapes: Shapes
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.shapes
    
            val styles: JetsnackStyles = JetsnackStyles
    
            val LocalJetsnackTheme: ProvidableCompositionLocal<JetsnackTheme>
                get() = LocalJetsnackThemeInstance
        }
    }
    
    val StyleScope.colors: JetsnackColors
        get() = LocalJetsnackTheme.currentValue.colors
    
    val StyleScope.typography: androidx.compose.material3.Typography
        get() = LocalJetsnackTheme.currentValue.typography
    
    val StyleScope.shapes: Shapes
        get() = LocalJetsnackTheme.currentValue.shapes
    
    internal val LocalJetsnackThemeInstance = staticCompositionLocalOf { JetsnackTheme() }
    
    @Composable
    fun JetsnackTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
        val colors = if (darkTheme) DarkJetsnackColors else LightJetsnackColors
        val theme = JetsnackTheme(colors = colors)
    
        CompositionLocalProvider(
            LocalJetsnackTheme provides theme,
        ) {
            MaterialTheme(
                typography = LocalJetsnackTheme.current.typography,
                shapes = LocalJetsnackTheme.current.shapes,
                content = content,
            )
        }
    }

  3. Composable işlevinizde JetsnackStyles öğesine erişin:

    @Composable
    fun CustomButton(modifier: Modifier,
                     style: Style = Style,
                     text: String) {
        val interactionSource = remember { MutableInteractionSource() }
        val styleState = remember(interactionSource) { MutableStyleState(interactionSource) }
    
        // Apply style to top level container in combination with incoming style from parameter.
        Box(modifier = modifier
            .clickable(
                interactionSource = interactionSource,
                indication = null,
                enabled = true,
                role = Role.Button,
                onClick = {
    
                },
            )
            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style)) {
            Text(text)
        }
    }

Küresel tema kullanımının yanı sıra, uygulamalarınıza Styles eklemek için alternatif stratejiler de vardır. Belirli arama siteleri için Styles satır içi özelliğinden yararlanabilir veya tam temalandırma özellikleri gerekmeyen durumlarda statik tanımlar kullanabilirsiniz. Styles, stilin tamamı temelde farklı olmadığı sürece koşullu olarak değiştirilmemelidir. Ayrı stil nesneleri arasında geçiş yapmak yerine, görsel tanım içinde dinamik jetonlara erişmeyi tercih etmelisiniz.