สิ่งที่ควรและไม่ควรทำกับสไตล์

หน้านี้อธิบายแนวทางปฏิบัติแนะนำสำหรับการใช้รูปแบบเพื่อให้ฐานของโค้ดมีความสอดคล้องกัน รวมถึงหลักการที่เราปฏิบัติตามขณะออกแบบ 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
}

ควร: แทนที่พารามิเตอร์ที่อิงตามภาพด้วยสไตล์

ลองแทนที่พารามิเตอร์ในคอมโพสเซเบิลด้วยพารามิเตอร์ 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
    }
}

ไม่ควร: ระบุพารามิเตอร์สไตล์ให้กับคอมโพสเซเบิลที่อิงตามเลย์เอาต์

แม้ว่าคุณจะระบุสไตล์ให้กับคอมโพสเซเบิลใดก็ได้ แต่คอมโพสเซเบิลที่อิงตามเลย์เอาต์หรือคอมโพสเซเบิลระดับหน้าจอไม่ควรยอมรับสไตล์ เนื่องจากผู้ใช้ไม่เข้าใจว่าสไตล์จะทำอะไรได้ในระดับนี้ สไตล์ได้รับการออกแบบมาสำหรับคอมโพเนนต์ ไม่ใช่เลย์เอาต์

ไม่ควร: สร้างสไตล์ใน Composition

ระบบจะอ่าน 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
}