কাস্টম মডিফায়ার তৈরি করা

Compose, সাধারণ আচরণের জন্য অনেক মডিফায়ার প্রদান করে, তবে আপনি নিজের কাস্টম মডিফায়ারও তৈরি করতে পারেন।

মডিফায়ারের একাধিক অংশ আছে:

  • একটি মডিফায়ার ফ্যাক্টরি
    • এটি Modifier-এর একটি এক্সটেনশন ফাংশন, যা আপনার মডিফায়ারের জন্য একটি ইডিওমেটিক API প্রদান করে এবং মডিফায়ারগুলিকে একসাথে চেইন করার অনুমতি দেয়। মডিফায়ার ফ্যাক্টরি মডিফায়ার এলিমেন্ট তৈরি করে যা কম্পোজ আপনার UI মডিফাই করতে ব্যবহার করে।
  • একটি মডিফায়ার এলিমেন্ট
    • এখানে আপনি মডিফায়ারের আচরণ প্রয়োগ করতে পারবেন।

প্রয়োজনীয় কার্যকারিতার উপর নির্ভর করে কাস্টম মডিফায়ার প্রয়োগ করার একাধিক উপায় আছে। প্রায়শই, কাস্টম মডিফায়ার প্রয়োগ করার সবচেয়ে সহজ উপায় হল কাস্টম মডিফায়ার ফ্যাক্টরি প্রয়োগ করা যা আগে থেকে সংজ্ঞায়িত অন্যান্য মডিফায়ার ফ্যাক্টরিকে একত্রিত করে। আপনার আরও কাস্টম আচরণের প্রয়োজন হলে, Modifier.Node API ব্যবহার করে মডিফায়ার এলিমেন্ট প্রয়োগ করুন, যা লোয়ার লেভেলের হলেও আরও বেশি ফ্লেক্সিবিলিটি প্রদান করে।

আগে থেকে থাকা মডিফায়ার একসাথে চেন করুন

আগে থেকে থাকা মডিফায়ার ব্যবহার করে কাস্টম মডিফায়ার তৈরি করা প্রায়ই সম্ভব হয়। যেমন, graphicsLayer মডিফায়ার ব্যবহার করে Modifier.clip() প্রয়োগ করা হয়। এই স্ট্র্যাটেজি আগে থেকে থাকা মডিফায়ার এলিমেন্ট ব্যবহার করে এবং আপনি নিজের কাস্টম মডিফায়ার ফ্যাক্টরি প্রদান করেন।

আপনার নিজস্ব কাস্টম মডিফায়ার প্রয়োগ করার আগে, আপনি একই কৌশল ব্যবহার করতে পারেন কিনা তা দেখুন।

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

অথবা, আপনি যদি দেখেন যে আপনি প্রায়শই একই মডিফায়ার গ্রুপ বারবার ব্যবহার করছেন, তাহলে আপনি সেগুলিকে আপনার নিজস্ব মডিফায়ারে র‍্যাপ করতে পারেন:

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

কম্পোজেবল মডিফায়ার ফ্যাক্টরি ব্যবহার করে কাস্টম মডিফায়ার তৈরি করা

এছাড়াও, আপনি আগে থেকে থাকা মডিফায়ারে ভ্যালু পাস করার জন্য কম্পোজেবল ফাংশন ব্যবহার করে কাস্টম মডিফায়ার তৈরি করতে পারবেন। এটি কম্পোজেবল মডিফায়ার ফ্যাক্টরি হিসেবে পরিচিত।

মডিফায়ার তৈরি করার জন্য কম্পোজেবল মডিফায়ার ফ্যাক্টরি ব্যবহার করলে, আপনি উচ্চ লেভেলের কম্পোজ API ব্যবহার করতে পারবেন, যেমন animate*AsState এবং অন্যান্য Compose স্টেট ব্যাকড অ্যানিমেশন API। যেমন, নিম্নলিখিত স্নিপেট এমন একটি মডিফায়ার দেখায় যা চালু/বন্ধ করার সময় আলফা পরিবর্তন অ্যানিমেট করে:

@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 }
}

আপনার কাস্টম মডিফায়ার যদি 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 ব্যবহার করে কাস্টম মডিফায়ার আচরণ প্রয়োগ করুন

Compose-এ মডিফায়ার তৈরি করার জন্য Modifier.Node হল একটি লোয়ার লেভেল API। এটি সেই একই API যা 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

Modifier.Node যা ObserverNode প্রয়োগ করে, সেটি 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-এর মাধ্যমে পারফর্ম্যান্স উন্নত করার মূল চাবিকাঠি; তাই, আপনাকে update পদ্ধতিতে নতুন নোড তৈরি করার পরিবর্তে আগে থেকে থাকা নোড আপডেট করতে হবে। আমাদের বৃত্তের উদাহরণে, নোডের রঙ আপডেট করা হয়েছে।

এছাড়াও, ModifierNodeElement প্রয়োগের ক্ষেত্রে equals এবং hashCode-ও প্রয়োগ করতে হবে। update will only get called if an equals comparison with the previous element returns false.

এটি করার জন্য আগের উদাহরণে ডেটা ক্লাস ব্যবহার করা হয়েছে। কোনও নোড আপডেট করতে হবে কিনা তা চেক করতে এইসব পদ্ধতি ব্যবহার করা হয়। আপনার এলিমেন্টে এমন প্রপার্টি থাকলে যা কোনও নোড আপডেট করতে হবে কিনা তা নির্ধারণে সাহায্য করে না অথবা আপনি বাইনারি কম্প্যাটিবিলিটির কারণে ডেটা ক্লাস এড়াতে চাইলে, আপনি ম্যানুয়ালি equals এবং hashCode প্রয়োগ করতে পারেন, যেমন, প্যাডিং মডিফায়ার এলিমেন্ট।

মডিফায়ার ফ্যাক্টরি

এটি আপনার মডিফায়ারের পাবলিক API সারফেস। বেশিরভাগ ইমপ্লিমেন্টেশন মডিফায়ার এলিমেন্ট তৈরি করে এবং মডিফায়ার চেনে যোগ করে:

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

সম্পূর্ণ উদাহরণ

Modifier.Node API ব্যবহার করে বৃত্ত আঁকার জন্য কাস্টম মডিফায়ার তৈরি করতে এই তিনটি অংশ একসাথে কাজ করে:

// 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 মডিফায়ার, CompositionLocal-এর মতো কম্পোজ স্টেট অবজেক্টে পরিবর্তন অটোমেটিক লক্ষ্য করে না। কম্পোজেবল ফ্যাক্টরি দিয়ে তৈরি মডিফায়ারের তুলনায় Modifier.Node মডিফায়ারের সুবিধা হল, এটি UI ট্রিতে মডিফায়ার যেখানে ব্যবহার করা হয়েছে সেখান থেকে কম্পোজিশন লোকালের ভ্যালু রিড করতে পারে, মডিফায়ার যেখানে অ্যালোকেট করা হয়েছে সেখান থেকে নয়, currentValueOf ব্যবহার করে।

তবে, মডিফায়ার নোড ইনস্ট্যান্স অটোমেটিক স্ট্যাটাস পরিবর্তন লক্ষ্য করে না। কম্পোজিশন লোকাল পরিবর্তনের ক্ষেত্রে অটোমেটিক প্রতিক্রিয়া জানাতে, আপনি একটি স্কোপের মধ্যে এর বর্তমান ভ্যালু পড়তে পারেন:

এই উদাহরণে, ব্যাকগ্রাউন্ডের রঙের উপর ভিত্তি করে ব্যাকগ্রাউন্ড আঁকার জন্য LocalContentColor-এর ভ্যালু পর্যবেক্ষণ করা হয়। যেহেতু ContentDrawScope স্ন্যাপশট পরিবর্তন লক্ষ্য করে, তাই LocalContentColor-এর ভ্যালু পরিবর্তন হলে এটি অটোমেটিক আবার আঁকে:

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

স্কোপের বাইরে স্ট্যাটাস পরিবর্তনের প্রতিক্রিয়া জানাতে এবং আপনার মডিফায়ার অটোমেটিক আপডেট করতে, ObserverModifierNode ব্যবহার করুন।

যেমন, LocalDensity-এ পরিবর্তন লক্ষ্য করতে Modifier.scrollable এই টেকনিক ব্যবহার করে। নিচের উদাহরণে একটি সহজ উদাহরণ দেখানো হয়েছে:

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 নোড অটোমেটিক বাতিল হয়ে যায় যখন তাদের সংশ্লিষ্ট 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)
        }
    }
}