Compose में कस्टम डिज़ाइन सिस्टम जोड़ना

हालांकि मटीरियल हमारा सुझाया गया डिज़ाइन सिस्टम है और Jetpack Compose हमारा सुझाव है कि मटीरियल लागू करने के बाद, आप उसका इस्तेमाल करने के लिए बाध्य नहीं होते हैं. मटीरियल बनाया गया है पूरी तरह से सार्वजनिक API पर काम करती है, इसलिए इसमें अपना खुद का डिज़ाइन सिस्टम उसी तरह से काम करते हैं.

इसके लिए, आप कई तरीके अपना सकते हैं:

आप चाहें, तो पसंद के मुताबिक डिज़ाइन वाले मटीरियल कॉम्पोनेंट इस्तेमाल करना जारी रखें सिस्टम. ऐसा करना मुमकिन है, लेकिन कुछ बातों का ध्यान रखना ज़रूरी है इस बारे में आपको ज़्यादा जानकारी मिलेगी.

MaterialTheme में इस्तेमाल किए जाने वाले निचले लेवल के कंस्ट्रक्शन और एपीआई के बारे में ज़्यादा जानने के लिए और कस्टम डिज़ाइन सिस्टम इस्तेमाल करना है, तो Compose में किसी थीम की एनाटॉमी गाइड देखें.

मटीरियल थीमिंग को बढ़ाना

मटीरियल को बारीकी से बनाने के लिए मॉडल मटीरियल थीमिंग ताकि मटीरियल से जुड़े दिशा-निर्देशों का पालन करना आसान और टाइप-सेफ़ हो जाए. हालांकि, यह रंग, टाइपोग्राफ़ी, और आकार सेट को बढ़ाने की सुविधा वैल्यू.

सबसे आसान तरीका है एक्सटेंशन प्रॉपर्टी जोड़ना:

// Use with MaterialTheme.colors.snackbarAction
val Colors.snackbarAction: Color
    get() = if (isLight) Red300 else Red700

// Use with MaterialTheme.typography.textFieldInput
val Typography.textFieldInput: TextStyle
    get() = TextStyle(/* ... */)

// Use with MaterialTheme.shapes.card
val Shapes.card: Shape
    get() = RoundedCornerShape(size = 20.dp)

इससे MaterialTheme इस्तेमाल करने वाले एपीआई के साथ एक जैसा अनुभव मिलता है. इसका एक उदाहरण Compose में बताई गई primarySurface जो primary और surface के बीच प्रॉक्सी के तौर पर काम करता है. यह इस पर निर्भर करता है कि Colors.isLight.

दूसरा तरीका यह है कि एक ऐसी एक्सटेंडेड थीम बनाई जाए जो MaterialTheme को “रैप” करे और इसके मूल्य.

मान लें कि आपको दो अतिरिक्त रंग — tertiary और onTertiary — जोड़ना है मौजूदा मटीरियल के रंगों को बनाए रखते हुए:

@Immutable
data class ExtendedColors(
    val tertiary: Color,
    val onTertiary: Color
)

val LocalExtendedColors = staticCompositionLocalOf {
    ExtendedColors(
        tertiary = Color.Unspecified,
        onTertiary = Color.Unspecified
    )
}

@Composable
fun ExtendedTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val extendedColors = ExtendedColors(
        tertiary = Color(0xFFA8EFF0),
        onTertiary = Color(0xFF002021)
    )
    CompositionLocalProvider(LocalExtendedColors provides extendedColors) {
        MaterialTheme(
            /* colors = ..., typography = ..., shapes = ... */
            content = content
        )
    }
}

// Use with eg. ExtendedTheme.colors.tertiary
object ExtendedTheme {
    val colors: ExtendedColors
        @Composable
        get() = LocalExtendedColors.current
}

यह MaterialTheme के इस्तेमाल से जुड़े एपीआई के जैसा है. इसमें कई थीम भी काम करती हैं MaterialTheme की तरह ही ExtendedTheme को नेस्ट किया जा सकता है.

मटीरियल कॉम्पोनेंट इस्तेमाल करना

मटीरियल थीमिंग को बढ़ाते समय, MaterialTheme की मौजूदा वैल्यू को बनाए रखा जाता है और मटीरियल कॉम्पोनेंट के लिए अब भी डिफ़ॉल्ट सेटिंग लागू है.

अगर आपको कॉम्पोनेंट में एक्सटेंडेड वैल्यू का इस्तेमाल करना है, तो उन्हें अपने कॉम्पोनेंट में रैप करें और कंपोज़ेबल फ़ंक्शन का इस्तेमाल करके, सीधे तौर पर उन वैल्यू को सेट करना होगा जिन्हें आपको बदलना है और कंपोज़ेबल कंपोज़ेबल में दूसरों को पैरामीटर के तौर पर दिखाना:

@Composable
fun ExtendedButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            containerColor = ExtendedTheme.colors.tertiary,
            contentColor = ExtendedTheme.colors.onTertiary
            /* Other colors use values from MaterialTheme */
        ),
        onClick = onClick,
        modifier = modifier,
        content = content
    )
}

इसके बाद, Button के इस्तेमाल को ExtendedButton से बदलें, जहां उचित.

@Composable
fun ExtendedApp() {
    ExtendedTheme {
        /*...*/
        ExtendedButton(onClick = { /* ... */ }) {
            /* ... */
        }
    }
}

मटीरियल सिस्टम बदलना

मटीरियल थीमिंग को बढ़ाने के बजाय, हो सकता है कि आप एक या उससे ज़्यादा थीम को बदलना चाहें सिस्टम — Colors, Typography या Shapes — को अपनी ज़रूरत के हिसाब से लागू करने के साथ, मदद मिलती है.

मान लें कि आपको रंग बनाए रखते हुए टाइप और आकृति सिस्टम को बदलना है सिस्टम:

@Immutable
data class ReplacementTypography(
    val body: TextStyle,
    val title: TextStyle
)

@Immutable
data class ReplacementShapes(
    val component: Shape,
    val surface: Shape
)

val LocalReplacementTypography = staticCompositionLocalOf {
    ReplacementTypography(
        body = TextStyle.Default,
        title = TextStyle.Default
    )
}
val LocalReplacementShapes = staticCompositionLocalOf {
    ReplacementShapes(
        component = RoundedCornerShape(ZeroCornerSize),
        surface = RoundedCornerShape(ZeroCornerSize)
    )
}

@Composable
fun ReplacementTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val replacementTypography = ReplacementTypography(
        body = TextStyle(fontSize = 16.sp),
        title = TextStyle(fontSize = 32.sp)
    )
    val replacementShapes = ReplacementShapes(
        component = RoundedCornerShape(percent = 50),
        surface = RoundedCornerShape(size = 40.dp)
    )
    CompositionLocalProvider(
        LocalReplacementTypography provides replacementTypography,
        LocalReplacementShapes provides replacementShapes
    ) {
        MaterialTheme(
            /* colors = ... */
            content = content
        )
    }
}

// Use with eg. ReplacementTheme.typography.body
object ReplacementTheme {
    val typography: ReplacementTypography
        @Composable
        get() = LocalReplacementTypography.current
    val shapes: ReplacementShapes
        @Composable
        get() = LocalReplacementShapes.current
}

मटीरियल कॉम्पोनेंट इस्तेमाल करना

जब Material का इस्तेमाल करके, MaterialTheme के एक या उससे ज़्यादा सिस्टम बदले गए हों कॉम्पोनेंट के रंग, टाइप या आकार में अनचाहे मटीरियल का इस्तेमाल किया जा सकता है.

अगर आपको कॉम्पोनेंट में रीप्लेसमेंट वैल्यू का इस्तेमाल करना है, तो उन्हें अपने कॉम्पोनेंट में रैप करें कंपोज़ेबल फ़ंक्शन का इस्तेमाल करके, सीधे उससे जुड़े सिस्टम के लिए वैल्यू सेट की जा सकती है और कंपोज़ेबल कंपोज़ेबल में दूसरों को पैरामीटर के तौर पर दिखाना.

@Composable
fun ReplacementButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        shape = ReplacementTheme.shapes.component,
        onClick = onClick,
        modifier = modifier,
        content = {
            ProvideTextStyle(
                value = ReplacementTheme.typography.body
            ) {
                content()
            }
        }
    )
}

इसके बाद, Button के इस्तेमाल को ReplacementButton से बदलें, जहां उचित.

@Composable
fun ReplacementApp() {
    ReplacementTheme {
        /*...*/
        ReplacementButton(onClick = { /* ... */ }) {
            /* ... */
        }
    }
}

पूरी तरह से पसंद के मुताबिक डिज़ाइन सिस्टम लागू करना

मटीरियल थीमिंग की जगह, अपनी पसंद के मुताबिक डिज़ाइन सिस्टम इस्तेमाल किया जा सकता है. ध्यान दें कि MaterialTheme से ये सिस्टम मिलते हैं:

  • Colors, Typography, और Shapes: मटीरियल थीमिंग सिस्टम
  • ContentAlpha: Text और Icon में ज़ोर देने के लिए अपारदर्शिता का लेवल
  • TextSelectionColors: Text और टेक्स्ट चुनने के लिए इस्तेमाल किए गए रंग TextField
  • Ripple और RippleTheme: Indication को लागू किया गया

अगर आपको मटीरियल कॉम्पोनेंट का इस्तेमाल जारी रखना है, तो आपको कुछ कॉम्पोनेंट बदलने होंगे इस्तेमाल कर सकते हैं या अपने कस्टम थीम या थीम में अनचाहे व्यवहार से बचने के लिए.

हालांकि, डिज़ाइन सिस्टम उन सिद्धांतों तक सीमित नहीं हैं जिन पर मटीरियल निर्भर करता है. आपने लोगों तक पहुंचाया मुफ़्त में मौजूदा सिस्टम में बदलाव कर सकता है और पूरी तरह से नए सिस्टम लागू कर सकता है — नई क्लास के साथ और टाइप — अन्य कॉन्सेप्ट को थीम के मुताबिक बनाने के लिए.

नीचे दिए गए कोड में, हम पसंद के मुताबिक कलर सिस्टम बनाते हैं. इसमें ग्रेडिएंट शामिल होते हैं (List<Color>), टाइप सिस्टम शामिल करें, ऊंचाई वाला कोई नया सिस्टम शामिल करें, और MaterialTheme के उपलब्ध कराए गए अन्य सिस्टम शामिल न करें:

@Immutable
data class CustomColors(
    val content: Color,
    val component: Color,
    val background: List<Color>
)

@Immutable
data class CustomTypography(
    val body: TextStyle,
    val title: TextStyle
)

@Immutable
data class CustomElevation(
    val default: Dp,
    val pressed: Dp
)

val LocalCustomColors = staticCompositionLocalOf {
    CustomColors(
        content = Color.Unspecified,
        component = Color.Unspecified,
        background = emptyList()
    )
}
val LocalCustomTypography = staticCompositionLocalOf {
    CustomTypography(
        body = TextStyle.Default,
        title = TextStyle.Default
    )
}
val LocalCustomElevation = staticCompositionLocalOf {
    CustomElevation(
        default = Dp.Unspecified,
        pressed = Dp.Unspecified
    )
}

@Composable
fun CustomTheme(
    /* ... */
    content: @Composable () -> Unit
) {
    val customColors = CustomColors(
        content = Color(0xFFDD0D3C),
        component = Color(0xFFC20029),
        background = listOf(Color.White, Color(0xFFF8BBD0))
    )
    val customTypography = CustomTypography(
        body = TextStyle(fontSize = 16.sp),
        title = TextStyle(fontSize = 32.sp)
    )
    val customElevation = CustomElevation(
        default = 4.dp,
        pressed = 8.dp
    )
    CompositionLocalProvider(
        LocalCustomColors provides customColors,
        LocalCustomTypography provides customTypography,
        LocalCustomElevation provides customElevation,
        content = content
    )
}

// Use with eg. CustomTheme.elevation.small
object CustomTheme {
    val colors: CustomColors
        @Composable
        get() = LocalCustomColors.current
    val typography: CustomTypography
        @Composable
        get() = LocalCustomTypography.current
    val elevation: CustomElevation
        @Composable
        get() = LocalCustomElevation.current
}

मटीरियल कॉम्पोनेंट इस्तेमाल करना

कोई MaterialTheme मौजूद न होने पर, Material के कॉम्पोनेंट को पहले की तरह इस्तेमाल करने से नतीजा मिलेगा सामग्री के रंग, टाइप, और आकार के साथ-साथ संकेत भी मिलते हैं.

अगर आपको कॉम्पोनेंट में कस्टम वैल्यू का इस्तेमाल करना है, तो उन्हें अपने कंपोज़ेबल में रैप करें फ़ंक्शन, सीधे संबंधित सिस्टम के लिए वैल्यू सेट करना, और कंपोज़ेबल में मौजूद पैरामीटर के तौर पर अन्य पैरामीटर जोड़े जा सकते हैं.

हमारा सुझाव है कि आप अपनी कस्टम थीम से सेट की गई वैल्यू को ऐक्सेस करें. इसके अलावा, अगर आपकी थीम Color, TextStyle, Shape या अन्य सिस्टम पर काम नहीं करती, उन्हें हार्डकोड कर सकता है.

@Composable
fun CustomButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            containerColor = CustomTheme.colors.component,
            contentColor = CustomTheme.colors.content,
            disabledContainerColor = CustomTheme.colors.content
                .copy(alpha = 0.12f)
                .compositeOver(CustomTheme.colors.component),
            disabledContentColor = CustomTheme.colors.content
                .copy(alpha = ContentAlpha.disabled)
        ),
        shape = ButtonShape,
        elevation = ButtonDefaults.elevatedButtonElevation(
            defaultElevation = CustomTheme.elevation.default,
            pressedElevation = CustomTheme.elevation.pressed
            /* disabledElevation = 0.dp */
        ),
        onClick = onClick,
        modifier = modifier,
        content = {
            ProvideTextStyle(
                value = CustomTheme.typography.body
            ) {
                content()
            }
        }
    )
}

val ButtonShape = RoundedCornerShape(percent = 50)

अगर आपने नए क्लास टाइप शुरू किए हैं, जैसे कि ग्रेडिएंट को दिखाने के लिए List<Color> — तो कॉम्पोनेंट को रैप करने के बजाय, उन्हें शुरुआत से लागू करना बेहतर हो सकता है. उदाहरण के लिए, देखें कि JetsnackButton JetSnacks सैंपल से ली जाएगी.