মডিফায়ার কম্পোজ করা

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

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

মডিফায়ার হল স্ট্যান্ডার্ড Kotlin অবজেক্ট। 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 এমিট করে, তা নিশ্চিত করা একটি সেরা পদ্ধতি। এটি করলে আপনার কোড আরও বেশি করে ব্যবহার করা যায় এবং এর আচরণ আরও বেশি অনুমানযোগ্য ও স্বজ্ঞাত হয়। আরও তথ্যের জন্য, Compose API নির্দেশিকা, এলিমেন্ট মডিফায়ার প্যারামিটার গ্রহণ করে ও সম্মান করে দেখুন।

মডিফায়ারের ক্রম গুরুত্বপূর্ণ

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

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

ক্লিক করলে, প্রান্তের চারপাশে প্যাডিং সহ পুরো অংশটি কাজ করে

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

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

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

বিল্ট-ইন মডিফায়ার

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

padding ও size

ডিফল্ট হিসেবে, Compose-এ দেওয়া লেআউট তার চাইল্ডকে র‍্যাপ করে। তবে, আপনি 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 মডিফায়ার সেট করুন। padding-এর start ও end প্যারামিটার লেআউটের দিক অনুসরণ করে। লেআউটের দিক যাই হোক না কেন, বাঁ বা ডানদিকের প্রান্ত প্যাড করতে, absolutePadding-এর left ও right প্যারামিটার ব্যবহার করুন।

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

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

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

অফসেট

কোনও লেআউটকে তার আসল পজিশনের সাপেক্ষে পজিশন করতে, offset মডিফায়ার যোগ করুন এবং x ও y অক্ষে অফসেট সেট করুন। অফসেট পজিটিভ এবং নন-পজিটিভ হতে পারে। 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 পারফর্ম্যান্স - যতটা সম্ভব দেরিতে রিড করা বিভাগটি পড়ুন।

Compose-এ স্কোপ সেফটি

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

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

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

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

Box-এ matchParentSize

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

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

নিচের উদাহরণে, চাইল্ড Spacer তার পেরেন্ট Box থেকে সাইজ নেয়, যা আবার সবচেয়ে বড় চাইল্ড থেকে সাইজ নেয়, এই ক্ষেত্রে ArtistCard।

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

ধূসর ব্যাকগ্রাউন্ড তার কন্টেনার পূরণ করছে

matchParentSize-এর পরিবর্তে fillMaxSize ব্যবহার করা হলে, Spacer পেরেন্টের জন্য অনুমোদিত সমস্ত উপলভ্য স্থান গ্রহণ করবে, যার ফলে পেরেন্ট প্রসারিত হবে এবং সমস্ত উপলভ্য স্থান পূরণ করবে।

স্ক্রিন জুড়ে ধূসর রঙের ব্যাকগ্রাউন্ড

Row-এ weight ও Column

প্যাডিং ও সাইজ সংক্রান্ত আগের বিভাগে আপনি যেমন দেখেছেন, ডিফল্ট হিসেবে, কম্পোজ করার উপযুক্ত আইটেমের সাইজ এটি যে কন্টেন্ট র‍্যাপ করছে তার দ্বারা নির্ধারিত হয়। আপনি 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 লেআউট, ড্রয়িং ও গ্রাফিক্সের মতো স্বতন্ত্র আচরণ, জেসচার-সম্পর্কিত, ফোকাস ও সিম্যান্টিক আচরণ এবং ডিভাইস ইনপুট ইভেন্ট প্রতিনিধিত্ব করে। এগুলি কোন ক্রমে আছে তা গুরুত্বপূর্ণ: যে মডিফায়ার এলিমেন্ট প্রথমে যোগ করা হয়, সেটি প্রথমে প্রয়োগ করা হবে।

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

  • কম্পোজেবল যা মডিফায়ার ব্যবহার করে, তার জন্য রিকম্পোজিশন হলে মডিফায়ারের রি-অ্যালোকেশন রিপিট করা হবে না
  • মডিফায়ার চেইন অনেক দীর্ঘ ও জটিল হতে পারে, তাই চেইনের একই ইনস্ট্যান্স আবার ব্যবহার করলে, সেগুলি তুলনা করার সময় Compose রানটাইমকে যে ওয়ার্কলোড সামলাতে হয়, তা কমানো যেতে পারে
  • এই এক্সট্র্যাকশন কোডবেস জুড়ে কোডের পরিচ্ছন্নতা, ধারাবাহিকতা এবং রক্ষণাবেক্ষণযোগ্যতা উন্নত করে

মডিফায়ার আবার ব্যবহার করার পেশাদার পদ্ধতি

আপনার নিজস্ব 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-এ স্কোপ সেফটি বিভাগটি দেখুন:

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)

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

আরও জানুন

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

মডিফায়ার কীভাবে ব্যবহার করতে হয় সেই বিষয়ে আরও প্র্যাক্টিস করতে, আপনি Compose codelab-এ বেসিক লেআউট দেখতে পারেন অথবা Now in Android রিপোজিটরি দেখতে পারেন।

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