Jetpack Compose'da Material You dinamik renk: duvar kağıdı kazandığında marka renginizi korumak
dynamicColorScheme() paletinizi kullanıcının duvar kağıdından türetilen biriyle değiştirir. Marka renklerini kaybetmek yerine uyumlaştırmak için pratik bir rehber.
Bir Compose uygulamasında dinamik rengi açtığınızda fark ettiğiniz ilk şey, markanızın kaybolduğudur. Granyn’in yeşili, Hydrame’in mavisi — gitti, yerine dynamicColorScheme()’in kullanıcının duvar kağıdından türettiği her ne ise o geldi. Standart mavi bir duvar kağıdında sorun yok. Turuncu veya eflatun bir duvar kağıdında ise, tüm görsel kimliği “yeşil olan” veya “mavi olan” olan bir uygulama artık telefondaki diğer her uygulama gibi görünüyor. Bu, Material You’nun tasarlandığı gibi çalışması ve rengin, kalabalık bir uygulama çekmecesinde kullanıcının uygulamanızı tanıma şekli olduğu durumlarda gerçek bir sorun.
Çözüm dinamik rengi kapatmak değil — sistem genelinde bir temaya geçiş yapmış kullanıcılar, bir uygulama uyum sağlamayı reddettiğinde bunu fark ediyor. Çözüm uyumlaştırmak: duvar kağıdından türetilen nötrleri ve etkileşim renklerini korurken, marka tonunuzu sessizce üzerine yazılmasına izin vermek yerine şemaya geri itmek.
dynamicColorScheme gerçekte ne veriyor
API 31+‘da, dynamicLightColorScheme(context) ve dynamicDarkColorScheme(context), sistemin duvar kağıdından çıkardığı tonal paletler olan android.R.color.system_accent1’den system_accent3’e kadar olanları okur ve size tam bir Material 3 ColorScheme verir. Kullanışlıdır çünkü işletim sisteminin geri kalanına karşı tutarlı görünmesi garanti edilen bir şema elde edersiniz. Ayrıca uygulamanızın hangi renk olması gerektiği hakkında sıfır bilgiye sahip bir şemadır.
@Composable
fun AppTheme(content: @Composable () -> Unit) {
val context = LocalContext.current
val colorScheme = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (isSystemInDarkTheme()) dynamicDarkColorScheme(context)
else dynamicLightColorScheme(context)
}
else -> if (isSystemInDarkTheme()) DarkColorScheme else LightColorScheme
}
MaterialTheme(colorScheme = colorScheme, content = content)
}
Çoğu eğitim materyali bu versiyonda durur ve bu tam olarak Hydrame’in su damlası ikonunun ve uygulama içi vurgu renginin, test ettiğim telefonların yarısında artık eşleşmemesine neden olan kod.
Değiştirmek yerine uyumlaştırmak
androidx.core.graphics.ColorUtils — ekstra bir bağımlılık gerektirmiyor, core-ktx içinde geliyor — sabit bir marka rengini, kimliğini kaybetmeden yeterince yakın hissettirecek kadar küçük adımlarla şemanın dinamik primary’sine doğru kaydırmak için kullanabileceğiniz bir blendHSL’e sahip. Material’ın kendi tasarım rehberliği buna “uyumlaştırma” diyor: sistemin sahip olmadığı bir rengi (bir hata kırmızısı, bir marka tonu, bir veri görselleştirme serisi) alıp, çakışmaması için en yakın dinamik role kısmen kaydırmak.
fun harmonize(designColor: Color, dynamicColor: Color, fraction: Float = 0.2f): Color {
val blended = ColorUtils.blendHSL(
designColor.toArgb(),
dynamicColor.toArgb(),
fraction,
)
return Color(blended)
}
Marka rengi #3B82F6 olan Hydrame’e uygulandığında:
val scheme = dynamicColorScheme(context)
val harmonizedAccent = harmonize(
designColor = Color(0xFF3B82F6),
dynamicColor = scheme.primary,
fraction = 0.15f,
)
fraction = 0.15f’te vurgu rengi hâlâ tartışmasız Hydrame mavisi olarak okunuyor, ama duvar kağıdının ürettiği hangi tonal nötr olursa olsun onunla savaşmak yerine rahatça yanında duruyor. Uyumlaştırılmış rengi yalnızca marka kimliğini taşıyan parçalar için kullanıyorum — uygulama ikonunun uygulama içi yankısı, su damlası ilerleme halkası, onboarding illüstrasyonları — ve geri kalan her şeyi (yüzeyler, metin, ayırıcılar, butonlar) sistemin kendi dinamik rollerinde bırakıyorum. Şemadaki her rengi uyumlaştırmak zaten dinamik rengin amacını baştan geçersiz kılıyor; hedef, aksi takdirde sisteme özgü bir palet içinde hayatta kalan bir tanınabilir vurgu rengi, tam bir marka devralması değil.
API 31 altında geri düşüş
Yukarıdaki geri düşüş mantığının üçte ikisi tek bir when dalı, ama orada pre-Android-12 şemanın nasıl göründüğü konusunda kasıtlı olmakta fayda var, çünkü bu bozulmuş bir dinamik şema değil — o cihazlarda sahip olduğunuz tek şema, nokta. Her uygulama için, dinamik bir şemanın ne üreteceğini yaklaştırmaya çalışmak yerine, doğrudan her uygulamanın tasarım tokenlerinde zaten var olan tam marka değerlerinden (Granyn’in #22C55E’si, Mintly’nin #F59E0B’si, Subly’nin #6366F1’i) beslenen, lightColorScheme(primary = ..., secondary = ..., ...) ile elle kurulmuş bir ColorScheme tutuyorum. Bu cihazlarda bu yazının konusu olan marka rengi sorunu zaten yok — tüm paletin sahibi sizsiniz — o yüzden elde edemeyeceğiniz dinamik davranışı simüle etmeye çaba harcamayın.
Sadece açık ve koyu değil, duvar kağıtları arasında test etmek
Dinamik renk, atlanması kolay bir test boyutu ekliyor: açık tema, koyu tema ve şimdi bir dizi duvar kağıdı tonu. Varsayılan Pixel duvar kağıdına karşı doğru görünen bir ekran, doygun bir duvar kağıdına karşı okunamaz bir kontrasta sahip olabilir, çünkü system_accent1 tonu ve krominansı birlikte kaydırır. Temalı bir ekranı yayınlamadan önce en azından soğuk bir duvar kağıdına, sıcak bir duvar kağıdına ve düşük doygunluklu gri tonlamalı bir duvar kağıdına karşı kontrol ediyorum — Ayarlar → Duvar Kağıdı ve stil → renk seç üzerinden, eşleşen bir görsel aramadan belirli bir tonu zorlamanıza izin veren bir yol. Uyumlaştırılmış vurgunuzun scheme.surface’e karşı kontrast oranı bu tonların herhangi birinde 4.5:1’in altına düşerse, yukarıdaki sabit fraction o ton aralığı için fazla agresiftir ve sabit bir sabit yerine bir kontrast kontrolüne ihtiyaç duyar:
fun harmonizeWithContrast(designColor: Color, scheme: ColorScheme, fraction: Float = 0.15f): Color {
val candidate = harmonize(designColor, scheme.primary, fraction)
return if (ColorUtils.calculateContrast(candidate.toArgb(), scheme.surface.toArgb()) >= 4.5) {
candidate
} else {
designColor // düşük kontrast göndermek yerine dokunulmamış marka rengine geri dön
}
}
Yayınlamadan önce kontrol edilecekler
- Marka rengi taşıyan her ekran (ikon yankıları, illüstrasyonlar, grafikler) uyumlaştırılmış bir değer kullanıyor, ham dinamik primary veya dokunulmamış marka hex’i değil.
- API 31 öncesi geri düşüş şeması, dinamik olanın sabit değerlerle bir kopyası değil, gerçek, elle ayarlanmış bir
ColorScheme. - Kontrast, sadece varsayılana karşı değil, en az bir doygun ve bir düşük krominanslı duvar kağıdına karşı kontrol ediliyor.
- Marka dışı yüzeyler (arka planlar, ayırıcılar, varsayılan butonlar) sistemin dinamik rollerinde dokunulmadan kalıyor — her şeyi uyumlaştırma dürtüsüne direnin.
Dinamik renk, bir uygulamayı jenerik bir telefon yerine belirli bir telefona ait hissettiren birkaç Android platform özelliğinden biri. Korunmaya değer. Sadece onu korumak için marka renginizi feda etmeye değmez.
// İlgili okumalar
Günlükten dahası
2026'da Android SplashScreen API'si: beyaz flaş olmadan soğuk başlangıç
Android'in SplashScreen API'sine pratik bir rehber — tema kurulumu, kimsenin okumadığı animasyonlu ikon boyut sınırları ve henüz hazır olmayan veriler için keepOnScreenCondition.
Android In-App Review API: sıkıcı olmadan puan istemek
Google'ın In-App Review API'sine pratik bir rehber — gerçekte nasıl çalıştığı, ne zaman tetiklenmesi gerektiği ve alışılmış 'bizi puanlayın' popup'ının Play Store puanınızı sessizce nasıl zedelediği.
2026'da Android'de erişilebilirlik: gerçekten uygulanan bir TalkBack ve Compose semantiği kontrol listesi
2026 için pratik bir Android erişilebilirlik kontrol listesi — TalkBack, Compose semantiği, dokunma hedefleri ve her ekranda yayınlamadan önce yaptığım test turu.