कस्टम मॉडिफ़ायर बनाएं

Compose में, सामान्य व्यवहारों के लिए कई मॉडिफ़ायर पहले से मौजूद होते हैं. हालांकि, आपके पास अपने हिसाब से कस्टम मॉडिफ़ायर बनाने का विकल्प भी होता है.

मॉडिफ़ायर के कई हिस्से होते हैं:

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

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

मौजूदा मॉडिफ़ायर को एक साथ जोड़ना

अक्सर, मौजूदा मॉडिफ़ायर का इस्तेमाल करके कस्टम मॉडिफ़ायर बनाए जा सकते हैं. उदाहरण के लिए, Modifier.clip() को graphicsLayer मॉडिफ़ायर का इस्तेमाल करके लागू किया जाता है. इस रणनीति में, पहले से मौजूद मॉडिफ़ायर एलिमेंट का इस्तेमाल किया जाता है. साथ ही, आपको अपना कस्टम मॉडिफ़ायर फ़ैक्ट्री देना होता है.

अपने कस्टम मॉडिफ़ायर को लागू करने से पहले, देखें कि क्या उसी रणनीति का इस्तेमाल किया जा सकता है.

fun Modifier.clip(shape: Shape) = graphicsLayer(shape = shape, clip = true)

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

fun Modifier.myBackground(color: Color) = padding(16.dp)
    .clip(RoundedCornerShape(8.dp))
    .background(color)

कंपोज़ेबल मॉडिफ़ायर फ़ैक्ट्री का इस्तेमाल करके कस्टम मॉडिफ़ायर बनाना

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

मॉडिफ़ायर बनाने के लिए कंपोज़ेबल मॉडिफ़ायर फ़ैक्ट्री का इस्तेमाल करने से, आपको animate*AsState और अन्य Compose state backed animation APIs जैसे ज़्यादा लेवल वाले कंपोज़ एपीआई इस्तेमाल करने की सुविधा भी मिलती है. उदाहरण के लिए, यहां दिए गए स्निपेट में एक ऐसा मॉडिफ़ायर दिखाया गया है जो चालू/बंद होने पर, ऐल्फ़ा में होने वाले बदलाव को ऐनिमेट करता है:

@Composable
fun Modifier.fade(enable: Boolean): Modifier {
    val alpha by animateFloatAsState(if (enable) 0.5f else 1.0f)
    return this then Modifier.graphicsLayer { this.alpha = alpha }
}

return graphicsLayer { this.alpha = alpha } का इस्तेमाल करके भी किया जा सकता है.

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

@Composable
fun Modifier.fadedBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

इस तरीके में कुछ कमियां हैं. इनके बारे में यहां बताया गया है.

CompositionLocal वैल्यू, मॉडिफ़ायर फ़ैक्ट्री की कॉल साइट पर तय की जाती हैं

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

@Composable
fun Modifier.myBackground(): Modifier {
    val color = LocalContentColor.current
    return this then Modifier.background(color.copy(alpha = 0.5f))
}

@Composable
fun MyScreen() {
    CompositionLocalProvider(LocalContentColor provides Color.Green) {
        // Background modifier created with green background
        val backgroundModifier = Modifier.myBackground()

        // LocalContentColor updated to red
        CompositionLocalProvider(LocalContentColor provides Color.Red) {

            // Box will have green background, not red as expected.
            Box(modifier = backgroundModifier)
        }
    }
}

अगर आपको लगता है कि मॉडिफ़ायर को इस तरह से काम नहीं करना चाहिए, तो इसके बजाय कस्टम Modifier.Node का इस्तेमाल करें. ऐसा इसलिए, क्योंकि कंपोज़िशन लोकल को इस्तेमाल की जा रही साइट पर सही तरीके से हल किया जाएगा और उन्हें सुरक्षित तरीके से ऊपर ले जाया जा सकता है.

कंपोज़ेबल फ़ंक्शन मॉडिफ़ायर कभी भी नहीं छोड़े जाते

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

ऐप्लिकेशन बनाने की सुविधा देने वाले फ़ंक्शन के मॉडिफ़ायर को, ऐप्लिकेशन बनाने की सुविधा देने वाले फ़ंक्शन में कॉल किया जाना चाहिए

सभी कंपोज़ेबल फ़ंक्शन की तरह, कंपोज़ेबल फ़ैक्ट्री मॉडिफ़ायर को कंपोज़िशन के अंदर से कॉल किया जाना चाहिए. इससे यह तय होता है कि किसी मॉडिफ़ायर को कहां तक ले जाया जा सकता है. ऐसा इसलिए, क्योंकि इसे कंपोज़िशन से बाहर कभी भी नहीं ले जाया जा सकता. इसके उलट, कंपोज़ेबल फ़ंक्शन से बाहर नॉन-कंपोज़ेबल मॉडिफ़ायर फ़ैक्ट्री को होस्ट किया जा सकता है. इससे उन्हें आसानी से फिर से इस्तेमाल किया जा सकता है और परफ़ॉर्मेंस को बेहतर बनाया जा सकता है:

val extractedModifier = Modifier.background(Color.Red) // Hoisted to save allocations

@Composable
fun Modifier.composableModifier(): Modifier {
    val color = LocalContentColor.current.copy(alpha = 0.5f)
    return this then Modifier.background(color)
}

@Composable
fun MyComposable() {
    val composedModifier = Modifier.composableModifier() // Cannot be extracted any higher
}

Modifier.Node का इस्तेमाल करके, कस्टम मॉडिफ़ायर के व्यवहार को लागू करना

Modifier.Node, Compose में मॉडिफ़ायर बनाने के लिए एक लोअर लेवल एपीआई है. यह वही एपीआई है जिसमें Compose अपने मॉडिफ़ायर लागू करता है. साथ ही, कस्टम मॉडिफ़ायर बनाने का सबसे बेहतर तरीका है.

Modifier.Node का इस्तेमाल करके कस्टम मॉडिफ़ायर लागू करना

Modifier.Node का इस्तेमाल करके कस्टम मॉडिफ़ायर लागू करने के तीन हिस्से होते हैं:

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

ModifierNodeElement क्लास स्टेटलेस होती हैं और हर बार रीकंपोज़िशन होने पर, नए इंस्टेंस असाइन किए जाते हैं. वहीं, Modifier.Node क्लास स्टेटफ़ुल हो सकती हैं और कई बार रीकंपोज़िशन होने पर भी बनी रहती हैं. साथ ही, इनका फिर से इस्तेमाल भी किया जा सकता है.

यहां दिए गए सेक्शन में, हर हिस्से के बारे में बताया गया है. साथ ही, एक सर्कल बनाने के लिए कस्टम मॉडिफ़ायर बनाने का उदाहरण दिखाया गया है.

Modifier.Node

Modifier.Node लागू करने पर (इस उदाहरण में, CircleNode), आपके कस्टम मॉडिफ़ायर की सुविधा लागू होती है.

// Modifier.Node
private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() {
    override fun ContentDrawScope.draw() {
        drawCircle(color)
    }
}

इस उदाहरण में, सर्कल को उस रंग से बनाया गया है जिसे मॉडिफ़ायर फ़ंक्शन में पास किया गया है.

कोई नोड, Modifier.Node के साथ-साथ शून्य या उससे ज़्यादा नोड टाइप लागू करता है. मॉडिफ़ायर को जिस फ़ंक्शन की ज़रूरत होती है उसके आधार पर, अलग-अलग नोड टाइप होते हैं. ऊपर दिए गए उदाहरण में, ड्रॉ करने की सुविधा होनी चाहिए. इसलिए, यह DrawModifierNode को लागू करता है. इससे ड्रॉ करने के तरीके को ओवरराइड किया जा सकता है.

ये टाइप उपलब्ध हैं:

नोड

इस्तेमाल

सैंपल लिंक

LayoutModifierNode

एक Modifier.Node, जो रैप किए गए कॉन्टेंट को मेज़र करने और लेआउट करने के तरीके को बदलता है.

नमूना

DrawModifierNode

एक Modifier.Node जो लेआउट के स्पेस में दिखता है.

नमूना

CompositionLocalConsumerModifierNode

इस इंटरफ़ेस को लागू करने से, आपका Modifier.Node कंपोज़िशन लोकल पढ़ सकता है.

नमूना

SemanticsModifierNode

एक Modifier.Node जो टेस्टिंग, ऐक्सेसिबिलिटी, और इसी तरह के इस्तेमाल के उदाहरणों में इस्तेमाल करने के लिए, सिमैंटिक्स की/वैल्यू जोड़ता है.

नमूना

PointerInputModifierNode

एक Modifier.Node जो PointerInputChanges. को स्वीकार करता है.

नमूना

ParentDataModifierNode

एक Modifier.Node, जो पैरंट लेआउट को डेटा उपलब्ध कराता है.

नमूना

LayoutAwareModifierNode

एक Modifier.Node जिसे onMeasured और onPlaced कॉलबैक मिलते हैं.

नमूना

GlobalPositionAwareModifierNode

ऐसा Modifier.Node जिसे कॉन्टेंट की ग्लोबल पोज़िशन में बदलाव होने पर, लेआउट के फ़ाइनल LayoutCoordinates के साथ onGloballyPositioned कॉलबैक मिलता है.

नमूना

ObserverModifierNode

ObserverNode लागू करने वाले Modifier.Node, onObservedReadsChanged को लागू करने का अपना तरीका उपलब्ध करा सकते हैं. इसे observeReads ब्लॉक में पढ़े गए स्नैपशॉट ऑब्जेक्ट में हुए बदलावों के जवाब में कॉल किया जाएगा.

नमूना

DelegatingNode

एक Modifier.Node, जो अन्य Modifier.Node इंस्टेंस को काम सौंप सकता है.

इससे कई नोड को एक साथ लागू करने में मदद मिल सकती है.

नमूना

TraversableNode

इसकी मदद से, Modifier.Node क्लास, एक ही तरह की क्लास या किसी खास कुंजी के लिए, नोड ट्री में ऊपर/नीचे जा सकती हैं.

नमूना

जब नोड से जुड़े एलिमेंट को अपडेट किया जाता है, तो नोड अपने-आप अमान्य हो जाते हैं. हमारा उदाहरण एक DrawModifierNode है. इसलिए, जब भी एलिमेंट पर कोई टाइम अपडेट होता है, तो नोड फिर से रेंडर होता है और उसका रंग सही तरीके से अपडेट होता है. नोड के अपने-आप अमान्य होने की सुविधा से ऑप्ट आउट करना सेक्शन में दिए गए तरीके से, अपने-आप अमान्य होने की सुविधा से ऑप्ट आउट किया जा सकता है.

ModifierNodeElement

ModifierNodeElement एक ऐसी क्लास है जिसमें बदलाव नहीं किया जा सकता. इसमें कस्टम मॉडिफ़ायर बनाने या अपडेट करने के लिए डेटा होता है:

// ModifierNodeElement
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)

    override fun update(node: CircleNode) {
        node.color = color
    }
}

ModifierNodeElement को लागू करने के लिए, इन तरीकों को बदलना होगा:

  1. create: यह फ़ंक्शन, आपके मॉडिफ़ायर नोड को इंस्टैंशिएट करता है. जब पहली बार आपका मॉडिफ़ायर लागू होता है, तब नोड बनाने के लिए इस फ़ंक्शन को कॉल किया जाता है. आम तौर पर, इसका मतलब नोड बनाना और उसे उन पैरामीटर के साथ कॉन्फ़िगर करना होता है जो मॉडिफ़ायर फ़ैक्ट्री को पास किए गए थे.
  2. update: इस फ़ंक्शन को तब कॉल किया जाता है, जब इस नोड में पहले से मौजूद जगह पर यह मॉडिफ़ायर दिया जाता है. हालांकि, इस दौरान प्रॉपर्टी में बदलाव होता है. यह क्लास के equals तरीके से तय होता है. पहले से बनाए गए मॉडिफ़ायर नोड को update कॉल में पैरामीटर के तौर पर भेजा जाता है. इस समय, आपको नोड की प्रॉपर्टी अपडेट करनी चाहिए, ताकि वे अपडेट किए गए पैरामीटर से मेल खाएं. नोड को इस तरह से फिर से इस्तेमाल करने की सुविधा, Modifier.Node की परफ़ॉर्मेंस को बेहतर बनाने में अहम भूमिका निभाती है. इसलिए, आपको Modifier.Node के तरीके में नया नोड बनाने के बजाय, मौजूदा नोड को अपडेट करना होगा.update सर्कल के उदाहरण में, नोड का रंग अपडेट किया गया है.

इसके अलावा, ModifierNodeElement को equals और hashCode भी लागू करना होगा. update को सिर्फ़ तब कॉल किया जाएगा, जब पिछले एलिमेंट के साथ तुलना करने पर 'बराबर है' फ़ंक्शन का नतीजा गलत हो.

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

मॉडिफ़ायर फ़ैक्ट्री

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

// Modifier factory
fun Modifier.circle(color: Color) = this then CircleElement(color)

पूरा उदाहरण

ये तीन हिस्से मिलकर, कस्टम मॉडिफ़ायर बनाते हैं. इससे Modifier.Node एपीआई का इस्तेमाल करके, सर्कल बनाया जा सकता है:

// Modifier factory
fun Modifier.circle(color: Color) = this then CircleElement(color)

// ModifierNodeElement
private data class CircleElement(val color: Color) : ModifierNodeElement<CircleNode>() {
    override fun create() = CircleNode(color)

    override fun update(node: CircleNode) {
        node.color = color
    }
}

// Modifier.Node
private class CircleNode(var color: Color) : DrawModifierNode, Modifier.Node() {
    override fun ContentDrawScope.draw() {
        drawCircle(color)
    }
}

Modifier.Node का इस्तेमाल करने की सामान्य स्थितियां

Modifier.Node की मदद से कस्टम मॉडिफ़ायर बनाते समय, आपको ये सामान्य समस्याएं आ सकती हैं.

कोई पैरामीटर नहीं

अगर आपके मॉडिफ़ायर में कोई पैरामीटर नहीं है, तो उसे कभी अपडेट करने की ज़रूरत नहीं होती. साथ ही, उसे डेटा क्लास होने की भी ज़रूरत नहीं होती. यहां एक ऐसे मॉडिफ़ायर को लागू करने का उदाहरण दिया गया है जो कंपोज़ेबल में तय की गई पैडिंग लागू करता है:

fun Modifier.fixedPadding() = this then FixedPaddingElement

data object FixedPaddingElement : ModifierNodeElement<FixedPaddingNode>() {
    override fun create() = FixedPaddingNode()
    override fun update(node: FixedPaddingNode) {}
}

class FixedPaddingNode : LayoutModifierNode, Modifier.Node() {
    private val PADDING = 16.dp

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val paddingPx = PADDING.roundToPx()
        val horizontal = paddingPx * 2
        val vertical = paddingPx * 2

        val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))

        val width = constraints.constrainWidth(placeable.width + horizontal)
        val height = constraints.constrainHeight(placeable.height + vertical)
        return layout(width, height) {
            placeable.place(paddingPx, paddingPx)
        }
    }
}

कंपोज़िशन के स्थानीय लोगों का रेफ़रंस

Modifier.Node मॉडिफ़ायर, Compose के स्टेटस ऑब्जेक्ट में हुए बदलावों को अपने-आप नहीं देखते. जैसे, CompositionLocal. Modifier.Node मॉडिफ़ायर का फ़ायदा यह है कि ये कंपोज़ेबल फ़ैक्ट्री की मदद से बनाए गए मॉडिफ़ायर से बेहतर होते हैं. ऐसा इसलिए, क्योंकि ये currentValueOf का इस्तेमाल करके, कंपोज़िशन लोकल की वैल्यू को पढ़ सकते हैं. कंपोज़िशन लोकल की वैल्यू को उस जगह से पढ़ा जाता है जहां मॉडिफ़ायर का इस्तेमाल आपके यूज़र इंटरफ़ेस (यूआई) ट्री में किया जाता है, न कि उस जगह से जहां मॉडिफ़ायर को असाइन किया जाता है.

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

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

class BackgroundColorConsumerNode :
    Modifier.Node(),
    DrawModifierNode,
    CompositionLocalConsumerModifierNode {
    override fun ContentDrawScope.draw() {
        val currentColor = currentValueOf(LocalContentColor)
        drawRect(color = currentColor)
        drawContent()
    }
}

स्कोप से बाहर के स्टेट में हुए बदलावों पर प्रतिक्रिया देने और अपने मॉडिफ़ायर को अपने-आप अपडेट करने के लिए, ObserverModifierNode का इस्तेमाल करें.

उदाहरण के लिए, Modifier.scrollable इस तकनीक का इस्तेमाल करके, LocalDensity में होने वाले बदलावों पर नज़र रखता है. यहां एक आसान उदाहरण दिया गया है:

class ScrollableNode :
    Modifier.Node(),
    ObserverModifierNode,
    CompositionLocalConsumerModifierNode {

    // Place holder fling behavior, we'll initialize it when the density is available.
    val defaultFlingBehavior = DefaultFlingBehavior(splineBasedDecay(UnityDensity))

    override fun onAttach() {
        updateDefaultFlingBehavior()
        observeReads { currentValueOf(LocalDensity) } // monitor change in Density
    }

    override fun onObservedReadsChanged() {
        // if density changes, update the default fling behavior.
        updateDefaultFlingBehavior()
    }

    private fun updateDefaultFlingBehavior() {
        val density = currentValueOf(LocalDensity)
        defaultFlingBehavior.flingDecay = splineBasedDecay(density)
    }
}

मॉडिफ़ायर को ऐनिमेट करना

Modifier.Node लागू करने वाले लोगों के पास coroutineScope का ऐक्सेस होता है. इससे Compose Animatable API का इस्तेमाल किया जा सकता है. उदाहरण के लिए, यह स्निपेट, पहले दिखाए गए CircleNode में बदलाव करता है, ताकि वह बार-बार फ़ेड इन और फ़ेड आउट हो:

class CircleNode(var color: Color) : Modifier.Node(), DrawModifierNode {
    private lateinit var alpha: Animatable<Float, AnimationVector1D>

    override fun ContentDrawScope.draw() {
        drawCircle(color = color, alpha = alpha.value)
        drawContent()
    }

    override fun onAttach() {
        alpha = Animatable(1f)
        coroutineScope.launch {
            alpha.animateTo(
                0f,
                infiniteRepeatable(tween(1000), RepeatMode.Reverse)
            ) {
            }
        }
    }
}

डेलिगेशन का इस्तेमाल करके, मॉडिफ़ायर के बीच स्थिति शेयर करना

Modifier.Node मॉडिफ़ायर, अन्य नोड को असाइन किए जा सकते हैं. इसके कई इस्तेमाल के उदाहरण हैं. जैसे, अलग-अलग मॉडिफ़ायर में सामान्य तौर पर लागू होने वाली चीज़ों को अलग करना. हालांकि, इसका इस्तेमाल मॉडिफ़ायर के बीच सामान्य स्थिति को शेयर करने के लिए भी किया जा सकता है.

उदाहरण के लिए, क्लिक किए जा सकने वाले मॉडिफ़ायर नोड को लागू करने का एक बुनियादी तरीका, जो इंटरैक्शन डेटा शेयर करता है:

class ClickableNode : DelegatingNode() {
    val interactionData = InteractionData()
    val focusableNode = delegate(
        FocusableNode(interactionData)
    )
    val indicationNode = delegate(
        IndicationNode(interactionData)
    )
}

नोड के अपने-आप अमान्य होने की सुविधा से ऑप्ट आउट करना

Modifier.Node कॉल अपडेट होने पर, Modifier.Node नोड अपने-आप अमान्य हो जाते हैं.ModifierNodeElement ज़्यादा जटिल मॉडिफ़ायर के लिए, हो सकता है कि आप इस सुविधा से ऑप्ट आउट करना चाहें, ताकि आपको यह तय करने का ज़्यादा कंट्रोल मिल सके कि मॉडिफ़ायर कब चरणों को अमान्य करता है.

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

इसका एक काल्पनिक उदाहरण यहां दिया गया है. इसमें एक ऐसा मॉडिफ़ायर दिखाया गया है जिसमें color, size, और onClick लैम्डा को प्रॉपर्टी के तौर पर इस्तेमाल किया गया है. यह मॉडिफ़ायर, सिर्फ़ ज़रूरी चीज़ों को अमान्य करता है. साथ ही, गैर-ज़रूरी चीज़ों को अमान्य नहीं करता:

class SampleInvalidatingNode(
    var color: Color,
    var size: IntSize,
    var onClick: () -> Unit
) : DelegatingNode(), LayoutModifierNode, DrawModifierNode {
    override val shouldAutoInvalidate: Boolean
        get() = false

    private val clickableNode = delegate(
        ClickablePointerInputNode(onClick)
    )

    fun update(color: Color, size: IntSize, onClick: () -> Unit) {
        if (this.color != color) {
            this.color = color
            // Only invalidate draw when color changes
            invalidateDraw()
        }

        if (this.size != size) {
            this.size = size
            // Only invalidate layout when size changes
            invalidateMeasurement()
        }

        // If only onClick changes, we don't need to invalidate anything
        clickableNode.update(onClick)
    }

    override fun ContentDrawScope.draw() {
        drawRect(color)
    }

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        val size = constraints.constrain(size)
        val placeable = measurable.measure(constraints)
        return layout(size.width, size.height) {
            placeable.place(0, 0)
        }
    }
}