সংশোধক রচনা করুন

মডিফায়ার আপনাকে কোনো কম্পোজেবলকে সজ্জিত বা বর্ধিত করার সুযোগ দেয়। মডিফায়ার আপনাকে এই ধরনের কাজগুলো করতে দেয়:

  • কম্পোজেবলটির আকার, লেআউট, আচরণ এবং চেহারা পরিবর্তন করুন।
  • তথ্য যোগ করুন, যেমন অ্যাক্সেসিবিলিটি লেবেল
  • ব্যবহারকারীর ইনপুট প্রক্রিয়া করুন
  • উচ্চ-স্তরের ইন্টারঅ্যাকশন যোগ করুন, যেমন কোনো এলিমেন্টকে ক্লিকযোগ্য, স্ক্রলযোগ্য, ড্র্যাগযোগ্য বা জুমযোগ্য করে তোলা।

মডিফায়ার হলো স্ট্যান্ডার্ড কোটলিন অবজেক্ট। Modifier ক্লাসের যেকোনো একটি ফাংশন কল করে একটি মডিফায়ার তৈরি করুন:

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

রঙিন পটভূমিতে দুই লাইনের লেখা, এবং লেখাগুলোর চারপাশে প্যাডিং রয়েছে।

আপনি এই ফাংশনগুলোকে একসাথে জুড়ে দিয়ে রচনা করতে পারেন:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

টেক্সটের পেছনের রঙিন ব্যাকগ্রাউন্ডটি এখন ডিভাইসের সম্পূর্ণ প্রস্থ জুড়ে বিস্তৃত।

উপরের কোডটিতে, বিভিন্ন মডিফায়ার ফাংশনের সম্মিলিত ব্যবহার লক্ষ্য করুন।

  • padding কোনো এলিমেন্টের চারপাশে ফাঁকা জায়গা তৈরি করে।
  • fillMaxWidth কম্পোজেবলটিকে তার প্যারেন্ট থেকে দেওয়া সর্বোচ্চ প্রস্থ জুড়ে পূরণ করতে সাহায্য করে।

এটি একটি উত্তম অভ্যাস যে আপনার সমস্ত কম্পোজেবল এলিমেন্ট একটি modifier প্যারামিটার গ্রহণ করবে এবং সেই মডিফায়ারটি তার প্রথম চাইল্ড এলিমেন্টে পাস করা হবে যা UI নির্গত করে। এটি করলে আপনার কোড আরও পুনঃব্যবহারযোগ্য হয় এবং এর আচরণ আরও অনুমানযোগ্য ও স্বজ্ঞাত হয়ে ওঠে। আরও তথ্যের জন্য, কম্পোজ এপিআই নির্দেশিকার “এলিমেন্টস অ্যাকসেপ্ট অ্যান্ড রেসপেক্ট আ মডিফায়ার প্যারামিটার” অংশটি দেখুন।

বিশেষকগুলির ক্রম গুরুত্বপূর্ণ

মডিফায়ার ফাংশনগুলোর ক্রম অত্যন্ত গুরুত্বপূর্ণ । যেহেতু প্রতিটি ফাংশন পূর্ববর্তী ফাংশন দ্বারা ফেরত দেওয়া Modifier পরিবর্তন আনে, তাই এই ক্রমটি চূড়ান্ত ফলাফলকে প্রভাবিত করে। চলুন এর একটি উদাহরণ দেখা যাক:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

প্রান্তের প্যাডিং সহ সম্পূর্ণ এলাকাটি ক্লিকে সাড়া দেয়।

উপরের কোডে, চারপাশের প্যাডিং সহ পুরো এলাকাটিই ক্লিকযোগ্য, কারণ clickable মডিফায়ারের পরে padding মডিফায়ারটি প্রয়োগ করা হয়েছে। যদি মডিফায়ারগুলোর ক্রম উল্টে দেওয়া হয়, তাহলে padding দ্বারা যুক্ত স্থানটি ব্যবহারকারীর ইনপুটে সাড়া দেয় না:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

লেআউটের প্রান্তের প্যাডিং এখন আর ক্লিকে সাড়া দিচ্ছে না।

অন্তর্নির্মিত মডিফায়ার

Jetpack Compose একটি কম্পোজেবলকে সাজাতে বা উন্নত করতে সাহায্য করার জন্য কিছু বিল্ট-ইন মডিফায়ারের একটি তালিকা প্রদান করে। এখানে কিছু সাধারণ মডিফায়ার দেওয়া হলো যা আপনি আপনার লেআউটগুলো সামঞ্জস্য করতে ব্যবহার করবেন।

padding এবং size

ডিফল্টরূপে, কম্পোজে প্রদত্ত লেআউটগুলি তাদের চাইল্ড লেআউটগুলিকে র‍্যাপ করে। তবে, আপনি size মডিফায়ার ব্যবহার করে একটি আকার নির্ধারণ করতে পারেন:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

মনে রাখবেন যে, আপনার নির্দিষ্ট করা আকারটি লেআউটের প্যারেন্টের সীমাবদ্ধতা পূরণ না করলে তা কার্যকর নাও হতে পারে। যদি আপনি চান যে আগত সীমাবদ্ধতা নির্বিশেষে কম্পোজেবল আকারটি স্থির থাকুক, তাহলে requiredSize মডিফায়ারটি ব্যবহার করুন:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

চাইল্ড ইমেজটি তার প্যারেন্টের দেওয়া সীমাবদ্ধতার চেয়ে বড়।

এই উদাহরণে, প্যারেন্টের height 100.dp সেট করা হলেও, Image উচ্চতা 150.dp হবে, কারণ requiredSize মডিফায়ারটি অগ্রাধিকার পায়।

যদি আপনি চান যে একটি চাইল্ড লেআউট তার প্যারেন্ট দ্বারা অনুমোদিত সম্পূর্ণ উচ্চতা পূরণ করুক, তাহলে fillMaxHeight মডিফায়ারটি যোগ করুন (Compose-এ fillMaxSize এবং fillMaxWidth ও রয়েছে):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

ছবিটির উচ্চতা তার প্যারেন্টের সমান।

কোনো এলিমেন্টের চারপাশে প্যাডিং যোগ করতে, একটি padding মডিফায়ার সেট করুন।

যদি আপনি টেক্সট বেসলাইনের উপরে এমনভাবে প্যাডিং যোগ করতে চান যাতে লেআউটের শীর্ষ থেকে বেসলাইন পর্যন্ত একটি নির্দিষ্ট দূরত্ব তৈরি হয়, তাহলে paddingFromBaseline মডিফায়ারটি ব্যবহার করুন:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

এর উপরে প্যাডিং সহ টেক্সট

অফসেট

একটি লেআউটকে তার মূল অবস্থানের সাপেক্ষে স্থাপন করতে, offset মডিফায়ারটি যোগ করুন এবং xy অক্ষে অফসেট সেট করুন। অফসেট ধনাত্মক এবং অ-ধনাত্মক উভয়ই হতে পারে। padding এবং offset মধ্যে পার্থক্য হলো, একটি কম্পোজেবলে offset যোগ করলে তার পরিমাপের কোনো পরিবর্তন হয় না:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

লেখাটি তার মূল কন্টেইনারের ডানদিকে সরে গেছে।

লেআউটের দিক অনুযায়ী offset মডিফায়ারটি আনুভূমিকভাবে প্রয়োগ করা হয়। বাম থেকে ডানের ক্ষেত্রে, একটি ধনাত্মক offset এলিমেন্টটিকে ডানে সরায়, আর ডান থেকে বামের ক্ষেত্রে এটি এলিমেন্টটিকে বামে সরায়। লেআউটের দিক বিবেচনা না করে যদি অফসেট সেট করার প্রয়োজন হয়, তবে absoluteOffset মডিফায়ারটি দেখুন, যেখানে একটি ধনাত্মক অফসেট মান সর্বদা এলিমেন্টটিকে ডানে সরায়।

offset মডিফায়ারের দুটি ওভারলোড রয়েছে - offset , যা প্যারামিটার হিসেবে অফসেটগুলো গ্রহণ করে এবং offset , যা একটি ল্যাম্বডা গ্রহণ করে। কখন এগুলোর প্রত্যেকটি ব্যবহার করতে হবে এবং পারফরম্যান্সের জন্য কীভাবে অপ্টিমাইজ করতে হবে, সে সম্পর্কে আরও বিস্তারিত তথ্যের জন্য “Compose performance - Defer reads as long as possible” অংশটি পড়ুন।

কম্পোজে স্কোপ নিরাপত্তা

Compose-এ এমন কিছু মডিফায়ার রয়েছে যা শুধুমাত্র নির্দিষ্ট কিছু কম্পোজেবলের চাইল্ডের উপর প্রয়োগ করা যায়। Compose কাস্টম স্কোপের মাধ্যমে এটি কার্যকর করে।

উদাহরণস্বরূপ, যদি আপনি Box Box আকারকে প্রভাবিত না করে কোনো চাইল্ডকে তার সমান বড় করতে চান, তাহলে matchParentSize মডিফায়ারটি ব্যবহার করুন। matchParentSize শুধুমাত্র BoxScope এ উপলব্ধ। তাই, এটি কেবল একটি Box প্যারেন্টের ভেতরের চাইল্ডের উপরই ব্যবহার করা যায়।

স্কোপ সেফটি আপনাকে এমন মডিফায়ার যোগ করা থেকে বিরত রাখে যা অন্যান্য কম্পোজেবল ও স্কোপে কাজ করবে না এবং বারবার চেষ্টা ও ভুলের মাধ্যমে সময় নষ্ট হওয়া থেকে বাঁচায়।

স্কোপড মডিফায়ারগুলো চাইল্ড সম্পর্কে প্যারেন্টের জানা উচিত এমন কিছু তথ্য প্যারেন্টকে অবহিত করে। এগুলোকে সাধারণত প্যারেন্ট ডেটা মডিফায়ারও বলা হয়। এদের অভ্যন্তরীণ গঠন জেনারেল পারপাস মডিফায়ারগুলো থেকে ভিন্ন, কিন্তু ব্যবহারের দৃষ্টিকোণ থেকে এই পার্থক্যগুলো গুরুত্বপূর্ণ নয়।

Box matchParentSize

উপরে যেমন উল্লেখ করা হয়েছে, যদি আপনি Box Box আকারকে প্রভাবিত না করে চাইল্ড লেআউটকে তার সমান আকারের করতে চান, তাহলে matchParentSize মডিফায়ারটি ব্যবহার করুন।

উল্লেখ্য যে, matchParentSize শুধুমাত্র একটি Box স্কোপের মধ্যেই উপলব্ধ, অর্থাৎ এটি কেবল Box কম্পোজেবলগুলোর সরাসরি চাইল্ডদের ক্ষেত্রেই প্রযোজ্য।

নীচের উদাহরণে, চাইল্ড Spacer তার আকার নেয় তার প্যারেন্ট Box (Box) থেকে, যা আবার তার আকার নেয় সবচেয়ে বড় চাইল্ডগুলো থেকে, এই ক্ষেত্রে ArtistCard থেকে।

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

ধূসর পটভূমি তার পাত্রটি পূরণ করছে

matchParentSize এর পরিবর্তে fillMaxSize ব্যবহার করা হলে, Spacer parent-এর জন্য বরাদ্দকৃত সমস্ত উপলব্ধ স্থান দখল করে নিত, যার ফলে parent-টি প্রসারিত হয়ে সমস্ত উপলব্ধ স্থান পূরণ করে ফেলত।

স্ক্রিন জুড়ে ধূসর পটভূমি

Row এবং Column weight

প্যাডিং এবং সাইজ সম্পর্কিত পূর্ববর্তী বিভাগে আপনি যেমন দেখেছেন, ডিফল্টরূপে, একটি কম্পোজেবল সাইজ তার দ্বারা আবৃত কন্টেন্টের মাধ্যমে নির্ধারিত হয়। আপনি weight মডিফায়ার ব্যবহার করে একটি কম্পোজেবল সাইজকে তার প্যারেন্টের মধ্যে নমনীয় করতে পারেন, যা শুধুমাত্র RowScope এবং ColumnScope এ উপলব্ধ।

ধরা যাক, একটি Row তে দুটি Box কম্পোজেবল রয়েছে। প্রথম বক্সটির weight দ্বিতীয়টির দ্বিগুণ, তাই এর প্রস্থও দ্বিগুণ। যেহেতু Row 210.dp চওড়া, প্রথম Box 140.dp চওড়া এবং দ্বিতীয়টি 70.dp চওড়া:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

ছবির প্রস্থ লেখার প্রস্থের দ্বিগুণ।

মডিফায়ার নিষ্কাশন এবং পুনঃব্যবহার

একটি কম্পোজেবলকে সজ্জিত বা বর্ধিত করতে একাধিক মডিফায়ারকে শৃঙ্খলিত করা যায়। এই শৃঙ্খলটি Modifier ইন্টারফেসের মাধ্যমে তৈরি করা হয়, যা একক Modifier.Elements এর একটি ক্রমবদ্ধ, অপরিবর্তনীয় তালিকা উপস্থাপন করে।

প্রতিটি Modifier.Element একটি স্বতন্ত্র আচরণকে প্রতিনিধিত্ব করে, যেমন লেআউট, ড্রয়িং এবং গ্রাফিক্স সংক্রান্ত আচরণ, সমস্ত অঙ্গভঙ্গি-সম্পর্কিত, ফোকাস এবং শব্দার্থ সংক্রান্ত আচরণ, সেইসাথে ডিভাইস ইনপুট ইভেন্ট। এদের ক্রম গুরুত্বপূর্ণ: যে মডিফায়ার এলিমেন্টগুলো প্রথমে যোগ করা হবে, সেগুলোই প্রথমে প্রয়োগ করা হবে।

কখনও কখনও একই মডিফায়ার চেইন ইনস্ট্যান্সগুলোকে ভেরিয়েবলে এক্সট্র্যাক্ট করে এবং উচ্চতর স্কোপে হোইস্ট করার মাধ্যমে একাধিক কম্পোজেবলে পুনরায় ব্যবহার করা সুবিধাজনক হতে পারে। এটি কয়েকটি কারণে কোডের পঠনযোগ্যতা বাড়াতে পারে বা আপনার অ্যাপের পারফরম্যান্স উন্নত করতে সাহায্য করতে পারে:

  • যেসব কম্পোজেবল মডিফায়ার ব্যবহার করে, সেগুলোর পুনর্গঠনের সময় মডিফায়ারগুলোর পুনর্বন্টনের পুনরাবৃত্তি হবে না।
  • মডিফায়ার চেইনগুলো সম্ভাব্যভাবে খুব দীর্ঘ এবং জটিল হতে পারে, তাই একটি চেইনের একই ইনস্ট্যান্স পুনরায় ব্যবহার করলে সেগুলোকে তুলনা করার সময় কম্পোজ রানটাইমের কাজের চাপ কমানো যায়।
  • এই নিষ্কাশন পদ্ধতি সমগ্র কোডবেস জুড়ে কোডের পরিচ্ছন্নতা, সামঞ্জস্য এবং রক্ষণাবেক্ষণযোগ্যতা বৃদ্ধি করে।

মডিফায়ার পুনঃব্যবহারের সর্বোত্তম অনুশীলন

আপনার নিজস্ব Modifier চেইন তৈরি করুন এবং একাধিক কম্পোজেবল কম্পোনেন্টে পুনরায় ব্যবহারের জন্য সেগুলোকে এক্সট্র্যাক্ট করুন। একটি মডিফায়ার শুধু সেভ করে রাখলে কোনো সমস্যা নেই, কারণ সেগুলো ডেটার মতো অবজেক্ট:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

ঘন ঘন পরিবর্তনশীল অবস্থা পর্যবেক্ষণ করার সময় মডিফায়ারগুলি নিষ্কাশন এবং পুনরায় ব্যবহার করা

কম্পোজেবলের ভেতরের ঘন ঘন পরিবর্তনশীল স্টেট, যেমন অ্যানিমেশন স্টেট বা scrollState , পর্যবেক্ষণ করার সময় উল্লেখযোগ্য পরিমাণে রিকম্পোজিশন সম্পন্ন হতে পারে। এক্ষেত্রে, আপনার মডিফায়ারগুলো প্রতিটি রিকম্পোজিশনে এবং সম্ভবত প্রতিটি ফ্রেমের জন্যও অ্যালোকেট হবে:

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

এর পরিবর্তে, আপনি মডিফায়ারটির একই ইনস্ট্যান্স তৈরি, এক্সট্র্যাক্ট ও পুনঃব্যবহার করতে পারেন এবং এটিকে কম্পোজেবল-এ এইভাবে পাস করতে পারেন:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

স্কোপ বহির্ভূত মডিফায়ার নিষ্কাশন এবং পুনঃব্যবহার

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

লেজি লেআউটের সাথে একত্রিত করলে এটি বিশেষভাবে উপকারী হতে পারে। বেশিরভাগ ক্ষেত্রে, আপনি চাইবেন আপনার সম্ভাব্য উল্লেখযোগ্য সংখ্যক আইটেমগুলোর সবগুলোরই যেন হুবহু একই মডিফায়ার থাকে:

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

স্কোপড মডিফায়ার নিষ্কাশন এবং পুনঃব্যবহার

নির্দিষ্ট কম্পোজেবলের আওতাভুক্ত মডিফায়ারগুলো নিয়ে কাজ করার সময়, আপনি সেগুলোকে সর্বোচ্চ সম্ভাব্য লেভেলে এক্সট্র্যাক্ট করতে পারেন এবং প্রয়োজন অনুযায়ী পুনরায় ব্যবহার করতে পারেন:

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

আপনার শুধুমাত্র নিষ্কাশিত, স্কোপড মডিফায়ারগুলোকেই একই-স্কোপের সরাসরি চাইল্ডদের কাছে পাস করা উচিত। এটি কেন গুরুত্বপূর্ণ, সে সম্পর্কে আরও তথ্যের জন্য Compose-এর Scope safety বিভাগটি দেখুন।

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

নিষ্কাশিত মডিফায়ারগুলির আরও শৃঙ্খল

আপনি .then() ফাংশনটি কল করে আপনার নিষ্কাশিত মডিফায়ার চেইনগুলোকে আরও যুক্ত বা সংযুক্ত করতে পারেন:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

শুধু মনে রাখবেন যে মডিফায়ারগুলোর ক্রম গুরুত্বপূর্ণ!

আরও জানুন

আমরা মডিফায়ারগুলোর একটি পূর্ণাঙ্গ তালিকা , তাদের প্যারামিটার এবং স্কোপসহ প্রদান করি।

মডিফায়ার ব্যবহারের আরও অনুশীলনের জন্য, আপনি কম্পোজ কোডল্যাবের বেসিক লেআউটগুলো দেখতে পারেন অথবা অ্যান্ড্রয়েড রিপোজিটরিতে 'Now' দেখতে পারেন।

কাস্টম মডিফায়ার এবং সেগুলি কীভাবে তৈরি করতে হয় সে সম্পর্কে আরও তথ্যের জন্য, "কাস্টম লেআউট - লেআউট মডিফায়ারের ব্যবহার" শীর্ষক ডকুমেন্টেশনটি দেখুন।

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}