الإجراءات المسموح بها وغير المسموح بها في"الأنماط"

توضّح هذه الصفحة أفضل الممارسات لاستخدام الأنماط التي تضمن الاتساق في جميع أقسام الرمز، بالإضافة إلى المبادئ التي اتّبعناها أثناء تصميم واجهات برمجة التطبيقات.

الإجراءات التي يُنصح بها

يُرجى اتّباع أفضل الممارسات التالية:

يجب: استخدام "الأنماط" للعناصر المرئية و"المعدّلات" للسلوكيات

استخدِم 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
}

الإجراء: استبدِل المَعلمات المستندة إلى العناصر المرئية بـ "نمط"

ننصحك باستبدال المَعلمات في العناصر القابلة للإنشاء بمَعلمة 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
}