מה לעשות ומה לא לעשות עם סגנונות

בדף הזה מוסברות שיטות מומלצות לעבודה עם סגנונות שמאפשרים לשמור על עקביות בבסיס הקוד, וגם עקרונות שיישמנו בתהליך התכנון של ממשקי ה-API.

מה לעשות

כדאי לפעול לפי השיטות המומלצות הבאות:

מומלץ: להשתמש בסגנונות לעיצוב חזותי ובמקשי צירוף להתנהגויות

משתמשים ב-Styles API להגדרת המראה (רקעים, שוליים פנימיים, גבולות) ושומרים את שינויי ההתנהגות כמו לוגיקת קליקים, זיהוי תנועות או נגישות.

מומלץ: לחשוף פרמטרים של סגנון במערכות עיצוב

לגבי רכיבים של מערכת עיצוב בהתאמה אישית, צריך לחשוף אובייקט Style אחרי פרמטר השינוי.

@Composable
fun GradientButton(
    modifier: Modifier = Modifier,
    // ✅ DO: for design system components, expose a style modifier to consumers to be able to customize the components
    style: Style = Style
) {
    // Consume the style
}

מומלץ: להחליף פרמטרים מבוססי-ויזואל בסגנון

כדאי להחליף את הפרמטרים ברכיבי ה-Composable בפרמטר יחיד מסוג Style. לדוגמה:

// Before
@Composable
fun OldButton(background: Color, fontColor: Color) {
}

// After
// ✅ DO: Replace visual-based parameters with a style that includes same properties
@Composable
fun NewButton(style: Style = Style) {
}

מומלץ: לתת עדיפות לסגנונות באנימציות

כדי לשפר את הביצועים בהשוואה לשימוש במאפייני שינוי, אפשר להשתמש בבלוק animate המובנה לעיצוב מבוסס-מצב עם אנימציות.

מומלץ: להשתמש בשיטה 'העדפה של הכתיבה האחרונה'

כדאי לנצל את העובדה שהמאפיינים style מחליפים את המאפיינים הקודמים במקום להוסיף אותם. אפשר להשתמש בפרמטר הזה כדי לבטל את ברירת המחדל של גבולות או רקעים של רכיבים בלי להשתמש בכמה פרמטרים.

מה לא לעשות

לא מומלץ להשתמש בדפוסים הבאים:

לא מומלץ: להשתמש בסגנונות עיצוב ללוגיקת אינטראקציה

אל תנסו לטפל בזיהוי של onClick או של תנועות במסגרת סגנון. הסגנונות מוגבלים להגדרות חזותיות שמבוססות על מצב, ולכן הם לא צריכים לטפל בלוגיקה עסקית. במקום זאת, הם צריכים להציג רק מראה שונה בהתאם למצב.

לא מומלץ: לספק סגנון ברירת מחדל כפרמטר ברירת מחדל

תמיד צריך להצהיר על פרמטרים של סגנון באמצעות style: Style = Style:

@Composable
fun BadButton(
    modifier: Modifier = Modifier,
    // ❌ DON'T set a default style here as a parameter
    style: Style = Style { background(Color.Red) }
) {
}

כדי לכלול פרמטר 'ברירת מחדל', צריך למזג את סגנון הפרמטר הנכנס עם ברירת המחדל שהוגדרה:

@Composable
fun GoodButton(
    modifier: Modifier = Modifier,
    // ✅ Do: always pass it as a Style, do not pass other defaults
    style: Style = Style
) {
    // ...
    val defaultStyle = Style { background(Color.Red) }
    // ✅ Do Combine defaults inside with incoming parameter
    Box(modifier = modifier.styleable(styleState, defaultStyle, style)) {
      // your logic
    }
}

לא מומלץ: לספק פרמטרים של סגנון לרכיבים הניתנים להרכבה שמבוססים על פריסה

אפשר לספק סגנון לכל רכיב שאפשר להרכיב, אבל לא מצפים שרכיבים שאפשר להרכיב שמבוססים על פריסה, או רכיבים שאפשר להרכיב ברמת המסך, יקבלו סגנון – לא ברור מנקודת מבט של צרכן מה סגנון יעשה ברמה הזו. הסגנונות מיועדים לרכיבים, ולא בהכרח לפריסות.

לא מומלץ: ליצור סגנונות בהלחנה

הערכים של CompositionLocals נקראים בנקודה שבה הסגנון מוגדר, ולא בנקודה שבה הוא נמצא בשימוש. כשמשתמשים בפועל בסגנון, יכול להיות שהמצב של CompositionLocal השתנה, ולכן הסגנון לא מדויק.

// DON'T - Create styles in Composition that access composition locals in this way - this will likely lead to issues when style is used / accessed, as it would not get updated when the value changes.
@Composable
fun containerStyle(): Style {
    val background = MaterialTheme.colorScheme.background
    val onBackground = MaterialTheme.colorScheme.onBackground
    return Style {
        background(background)
        contentColor(onBackground)
    }
}

// Do: Instead, Create StyleScope extension functions for your subsystems to access themed composition Locals
val StyleScope.colors: JetsnackColors
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.colors

val StyleScope.typography: androidx.compose.material3.Typography
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.typography
val StyleScope.shapes: Shapes
    get() = JetsnackTheme.LocalJetsnackTheme.currentValue.shapes
// Access CompositionLocals
val button = Style {
    background(colors.brandSecondary)
    shape(shapes.small)
}

מומלץ: ליצור סגנון אחד לשינויים בערכים של מערכת משנה

לדוגמה, אם עוברים בין מצב כהה למצב בהיר, צריך לשלוח שאילתה לגבי ערכי עיצוב קיימים (באמצעות CompositionLocal) כדי לשנות את Style באופן דינמי:

// Do: Use CompositionLocals or themed values to create a single style
val buttonStyle = Style {
    background(colors.brandSecondary)
    shape(shapes.small)
}

מומלץ: להחליף סגנונות שלמים כשהרכיב שונה באופן מהותי בהגדרות העיצוב

אתם יכולים להחליף אובייקטים שלמים של סגנון ברמת העיצוב אם מדובר בעיצובים שונים באופן מהותי.

לדוגמה, אם אתם יוצרים אפליקציה עם ערכות נושא שונות לכל מוצר או דף, ויש הרבה מאפיינים שונים של סגנון, אפשר להחליף ערכות שלמות של סגנונות ברמת ערכת הנושא.

// DO Switch out whole styles when many properties differ - if Product A and Product B are two white labelled apps that provide different Themes.
val productBThemedButton = Style {
    shape(shapes.small)
    background(colors.brandSecondary)
    // other properties are fundamentally different
}

val productAThemedButton = Style {
    shape(shapes.large)
    background(colors.brand)
    // other properties are fundamentally different
}