بنقرة واحدة
m3-dynamic-color-resolution
How to properly resolve Material 3 Dynamic Colors in this Android/Kotlin project
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
How to properly resolve Material 3 Dynamic Colors in this Android/Kotlin project
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Guidelines for managing Varisankya releases across Beta and Production tracks.
How to safely record extra payments without advancing the Varisankya subscription next due date.
Instructions for extracting the Play Store upload keystore, Firebase JSON, and passwords from Bitwarden.
Standard operating procedure for concluding an AI agent session and ensuring workspace integrity.
Guidelines for compiling Varisankya from the CLI without Android Studio.
How to correctly handle custom dates for extra subscription payments using MaterialDatePicker.
| name | M3 Dynamic Color Resolution |
| description | How to properly resolve Material 3 Dynamic Colors in this Android/Kotlin project |
[!IMPORTANT] Hybrid Design System:
- App: Uses strict MONOCHROME (Black/Gray/White) identity.
- Widget: Uses Wallpaper Orchestration (Dynamic Colors) to natively integrate with the user's Home Screen (API 31+).
This skill explains how to resolve these two distinct systems.
The app uses a strict mapping in colors.xml and ThemeHelper.kt to ensure color consistency across Activities, Bottom Sheets, and Widgets.
Widgets MUST use the dynamic_* color aliases located in res/values/colors.xml and res/values-v31/colors.xml. These aliases resolve to wallpaper-based system tokens on Android 12+.
| Logical Role | Resource Name | Purpose |
|---|---|---|
| Root Surface | @color/dynamic_widget_background | Widget container base |
| Hero Surface | @color/dynamic_hero_background | Elevated "Next Payment" block |
| Pill Accent | @color/dynamic_pill_background | High-contrast status pill |
| Text Primary | @color/dynamic_text_primary | Names and titles |
| Text Accent | @color/dynamic_text_accent | Sub-labels and dates |
Night Mode Parity: Ensure values-night and values-night-v31 provide sufficient contrast (e.g. Hero Light -> Pill Dark).
To achieve the "Breezy" M3E look, use the correct container role for the background:
| Role | Attribute | Usage |
|---|---|---|
| Base | colorSurfaceContainerLow | Main screen window background. |
| Card | colorSurfaceContainer | Subscription items (Standard). |
| Pill | colorSurfaceContainerHigh | Highlight bubbles inside cards. |
| Hero | colorPrimary | Highest impact highlights (Active/Urgent). |
A common issue in this project is that BaseActivity calls setTheme() to handle custom fonts (System vs Google Sans). This manual setTheme() call wipes out the Dynamic Color overlay applied by the Application class.
Solution:
You MUST re-apply dynamic colors after calling setTheme():
override fun onCreate(savedInstanceState: Bundle?) {
// ... logic to check font pref ...
setTheme(themeId) // <--- This wipes Dynamic Colors
// VITAL: Re-apply overlay immediately
com.google.android.material.color.DynamicColors.applyToActivityIfAvailable(this)
super.onCreate(savedInstanceState)
}
Direct references to com.google.android.material.R.attr.colorPrimary often cause "Unresolved reference" errors.
Use the ThemeHelper utility class located at:
app/src/main/java/com/hora/varisankya/util/ThemeHelper.kt
| Method | M3 Attribute | Usage |
|---|---|---|
getPrimaryColor(context) | colorPrimary | Maximum contrast (Tier 1) |
getOnPrimaryColor(context) | colorOnPrimary | Text on Primary |
getTertiaryColor(context) | colorTertiary | High contrast (Tier 2) |
getOnTertiaryColor(context) | colorOnTertiary | Text on Tertiary |
getSecondaryContainerColor(context) | colorSecondaryContainer | Medium contrast (Tier 3) |
getOnSecondaryContainerColor(context) | colorOnSecondaryContainer | Text on SecondaryContainer |
getSurfaceVariantColor(context) | colorSurfaceVariant | Low contrast (Tier 4/Inactive) |
getOnSurfaceVariantColor(context) | colorOnSurfaceVariant | Text on SurfaceVariant |
getOnSurfaceColor(context) | colorOnSurface | Standard text |
getSurfaceContainerHighColor(context) | colorSurfaceContainerHigh | Distinct surface for chips/cards |
getOutlineVariantColor(context) | colorOutlineVariant | Progress bar tracks |
ThemeHelper uses Resources.getIdentifier() with context.packageName to find merged theme attributes:
// CORRECT: Uses app's package name to find merged M3 attributes
val attrId = context.resources.getIdentifier(attrName, "attr", context.packageName)
// WRONG: Material package doesn't contain these at runtime
val attrId = context.resources.getIdentifier(attrName, "attr", "com.google.android.material")
// WRONG: Causes Kotlin compilation errors
MaterialColors.getColor(context, com.google.android.material.R.attr.colorPrimary, fallback)
import com.hora.varisankya.util.ThemeHelper
// In any Activity, Fragment, or Adapter
val primaryColor = ThemeHelper.getPrimaryColor(context)
val tertiaryColor = ThemeHelper.getTertiaryColor(context)
// Apply to views
holder.pillContainer.setCardBackgroundColor(primaryColor)
chip.chipBackgroundColor = ColorStateList.valueOf(tertiaryColor)
For XML resources, use ?attr/ syntax which resolves correctly:
<item android:color="?attr/colorTertiary" android:state_checked="true"/>
<item android:color="?attr/colorSurfaceVariant"/>
These are defined in:
res/color/chip_background_color.xmlres/color/chip_stroke_color.xmlres/color/chip_text_color.xmlSymptom:
e: Unresolved reference 'colorPrimary'.
Cause: Kotlin cannot resolve com.google.android.material.R.attr.colorPrimary at compile time.
Solution: Use ThemeHelper instead of direct R.attr references.
Symptom: All UI elements appear gray instead of theme colors.
Cause: Using wrong package name in getIdentifier():
// WRONG - returns 0, triggering fallback color
getIdentifier(name, "attr", "com.google.android.material")
Solution: Use app's package name:
// CORRECT
getIdentifier(name, "attr", context.packageName)
Symptom: Chips created with ContextThemeWrapper(..., R.style.Widget_App_Chip) don't show selected colors.
Cause: XML selectors with ?attr/ don't resolve through ContextThemeWrapper properly.
Solution: Apply colors programmatically using ThemeHelper (see chip-styling skill).
To achieve the "Breezy" M3E look, use the correct container role for the background:
| Role | Attribute | Usage |
|---|---|---|
| Base | colorSurfaceContainerLow | Main screen window background. |
| Card | colorSurfaceContainer | Subscription items, standard blocks. |
| Pill | colorSurfaceContainerHigh | Highlight bubbles, amount chips inside cards. |
| Active | colorPrimary | Highest urgency/active status highlights. |