শৈলী দিয়ে থিমিং

স্টাইল ব্যবহার করে আপনার অ্যাপ তৈরি করার বিভিন্ন উপায় রয়েছে। আপনি কোনটি বেছে নেবেন, তা নির্ভর করে ম্যাটেরিয়াল ডিজাইন গ্রহণের ক্ষেত্রে আপনার অ্যাপটি কোন অবস্থানে আছে তার উপর:

  1. সম্পূর্ণ কাস্টম ডিজাইন সিস্টেম, ম্যাটেরিয়াল ডিজাইন ব্যবহার করা হয়নি।
    • সুপারিশ : এমন কম্পোনেন্ট স্টাইল নির্ধারণ করুন যা থিম থেকে মান গ্রহণ করে, এবং ডিজাইন সিস্টেম কম্পোনেন্টগুলিতে স্টাইল প্যারামিটারগুলি উন্মুক্ত করুন।
  2. উপাদান নকশা ব্যবহার করে
    • সুপারিশ : স্টাইলের সাথে একীভূত হওয়ার জন্য ম্যাটেরিয়াল গৃহীত হওয়া পর্যন্ত অপেক্ষা করুন। যেখানে সম্ভব, আপনার নিজের কম্পোনেন্টগুলিতে স্টাইল ব্যবহার করুন।

স্টাইল লেয়ার

প্রচলিত কম্পোজ মডেলে, কাস্টমাইজেশন প্রায়শই MaterialTheme ) দ্বারা প্রদত্ত গ্লোবাল টোকেন (রঙ এবং টাইপোগ্রাফি) ওভাররাইড করার উপর, অথবা যেখানে সম্ভব সেখানে একটি ডিজাইন সিস্টেম কম্পোজেবলের প্রোপার্টিগুলোকে র‍্যাপ ও ওভাররাইড করার উপর ব্যাপকভাবে নির্ভর করে। কখনও কখনও, ম্যাটেরিয়াল লেয়ারের মধ্যে এমন কিছু প্রোপার্টি থাকে যা সাবসিস্টেম বা প্যারামিটারের মাধ্যমে প্রকাশ করা হয় না, বরং কম্পোনেন্টটির মধ্যেই হার্ডকোডেড ডিফল্ট হিসেবে থাকে।

স্টাইলস এপিআই-এর মাধ্যমে অ্যাবস্ট্রাকশনের একটি নতুন স্তর যুক্ত হয়েছে, যা সাবসিস্টেম এবং কম্পোনেন্টগুলোর মধ্যে একটি সেতুবন্ধন হিসেবে কাজ করে: স্টাইলস

স্তর দায়িত্ব উদাহরণ
সাবসিস্টেম মান নামযুক্ত মান val Primary = Color(0xFF34A85E)
পারমাণবিক শৈলী এমন স্টাইল যা ঠিক একটি প্রপার্টি পরিবর্তন করে val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then largeSize then interactiveShadowAtomic
উপাদান শৈলী উপাদান-নির্দিষ্ট কনফিগারেশন প্রাইমারি ব্যাকগ্রাউন্ড এবং 16dp প্যাডিং সহ একটি বাটন। val buttonStyle = Style { contentPadding(16.dp) shape(RoundedCornerShape(8.dp)) background(Color.Blue) }
উপাদান কার্যকরী UI উপাদান যা একটি স্টাইল গ্রহণ করে। Button(style = buttonStyle) { ... }
নতুন লেয়ার সংযোজনের সাথে স্টাইল ব্যবহার করে থিমিং দেখানোর ডায়াগ্রাম।
চিত্র ১. একটি কম্পোনেন্টের উদাহরণ এবং এটি কীভাবে একটি থিম থেকে স্টাইল গ্রহণ করে।

পারমাণবিক বনাম মনোলিথিক শৈলী

স্টাইলস এপিআই (Styles API) ব্যবহার করে, আপনি একটি স্টাইলকে আলাদা আলাদা অ্যাটমিক স্টাইলে ভেঙে ফেলতে পারেন। baseButtonStyle মতো জটিল, কম্পোনেন্ট-নির্দিষ্ট স্টাইল সংজ্ঞায়িত করার পরিবর্তে, আপনি ছোট, একক-উদ্দেশ্যমূলক ইউটিলিটি স্টাইলও তৈরি করতে পারেন। এগুলোই আপনার 'অ্যাটম' হিসেবে কাজ করে।

// Define single-purpose "atomic" styles
val paddingAtomic = Style {
    contentPadding(16.dp)
}
val roundedCornerShapeAtomic = Style {
    shape(RoundedCornerShape(8.dp))
}
val primaryBackgroundAtomic = Style {
    background(Color.Blue)
}
val largeSizeAtomic = Style {
    size(100.dp, 40.dp)
}
val interactiveShadowAtomic = Style {
    hovered {
        animate {
            dropShadow(
                Shadow(
                    offset = DpOffset(
                        0.dp,
                        0.dp
                    ),
                    radius = 2.dp,
                    spread = 0.dp,
                    color = Color.Blue,
                )
            )
        }
    }
}

'then' ব্যবহার করে রচনা

নতুন স্টাইলস এপিআই-এর অন্যতম শক্তিশালী বৈশিষ্ট্য হলো then অপারেটর, যা আপনাকে একাধিক Style অবজেক্ট একত্রিত করতে দেয়। এর মাধ্যমে আপনি অ্যাটমিক ইউটিলিটি ক্লাস ব্যবহার করে একটি কম্পোনেন্ট তৈরি করতে পারেন।

ঐতিহ্যবাহী (অ-পারমাণবিক) :

// One large monolithic style
val buttonStyle = Style {
    contentPadding(16.dp)
    shape(RoundedCornerShape(8.dp))
    background(Color.Blue)
}

পারমাণবিক পুনর্গঠন :

// Combine atoms to create the final appearance
val buttonStyle = paddingAtomic then roundedCornerShapeAtomic then primaryBackgroundAtomic then interactiveShadowAtomic

আপনার ডিজাইন সিস্টেমে শৈলী গ্রহণ করুন

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

স্টাইল সহ কাস্টম ডিজাইন সিস্টেম

বিবেচনা করুন যখন : আপনাকে একটি বিশদ ব্র্যান্ড গাইড দেওয়া হয়েছে যা ম্যাটেরিয়াল ডিজাইনের উপর ভিত্তি করে তৈরি নয়, এবং আপনি ম্যাটেরিয়াল ডিজাইন ব্যবহার করার পরিকল্পনা করছেন না

কৌশল : একটি সম্পূর্ণ কাস্টম ডিজাইন সিস্টেম বাস্তবায়ন করা এবং স্টাইলগুলোকে থিমের অংশ হিসেবে প্রকাশ করা

আপনি যদি আপনার প্রধান ডিজাইন সিস্টেম ল্যাঙ্গুয়েজ হিসেবে ম্যাটেরিয়াল ব্যবহার না করেন, তবে এই বিকল্পটিই হলো কাস্টম পথ। আপনি ভিজ্যুয়াল ডেফিনিশনের জন্য MaterialTheme পুরোপুরি এড়িয়ে যান এবং ইতিমধ্যেই আপনার নিজস্ব কাস্টম থিম তৈরি করে রেখেছেন। আপনি একটি CompanyTheme তৈরি করেন যা আপনার স্টাইলগুলোর জন্য একটি কন্টেইনার হিসেবে কাজ করে।

  • এটি যেভাবে কাজ করে : একটি CompanyTheme অবজেক্ট তৈরি করুন যা আপনার সিস্টেমের প্রতিটি কম্পোনেন্টের জন্য Style অবজেক্ট ধারণ করে। আপনার কম্পোনেন্টগুলো (Material লজিকের র‍্যাপার অথবা কাস্টম Box বা Layout ইমপ্লিমেন্টেশন) সরাসরি এই স্টাইলগুলো ব্যবহার করে এবং আপনার ডিজাইন সিস্টেমের ব্যবহারকারীদের জন্য একটি Style প্যারামিটার উন্মুক্ত করে।
  • স্টাইল লেয়ার : স্টাইল হলো আপনার ডিজাইন সিস্টেমের প্রাথমিক সংজ্ঞা। টোকেন হলো নামযুক্ত ভেরিয়েবল যা এই স্টাইলগুলিতে ইনপুট হিসেবে ব্যবহৃত হয়। এটি গভীর কাস্টমাইজেশনের সুযোগ দেয়, যেমন অবস্থার পরিবর্তনের জন্য স্বতন্ত্র অ্যানিমেশন নির্ধারণ করা (উদাহরণস্বরূপ, প্রেস করার সময় স্কেল এবং রঙের অ্যানিমেশন)।

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

  1. একটি Styles ক্লাস তৈরি করুন যা আপনার অ্যাপ্লিকেশনের বিভিন্ন স্টাইল সংরক্ষণ করবে এবং ডিফল্টগুলো তৈরি করবে। উদাহরণস্বরূপ, Jetsnack অ্যাপে ক্লাসটির নাম JetsnackStyles :

    object JetsnackStyles{
        val buttonStyle: Style = Style {
            shape(shapes.medium)
            background(colors.brand)
            contentColor(colors.textPrimary)
            contentPaddingVertical(8.dp)
            contentPaddingHorizontal(24.dp)
            textStyle(typography.labelLarge)
            disabled {
                animate {
                    background(colors.brandSecondary)
                }
            }
        }
        val cardStyle: Style = Style {
            shape(shapes.medium)
            background(colors.uiBackground)
            contentColor(colors.textPrimary)
        }
    }

  2. আপনার সামগ্রিক থিমের অংশ হিসেবে Styles প্রদান করুন, এবং সাবসিস্টেমগুলো অ্যাক্সেস করার জন্য StyleScope এ হেল্পার এক্সটেনশন ফাংশনগুলো উন্মুক্ত করুন:

    @Immutable
    class JetsnackTheme(
        val colors: JetsnackColors = LightJetsnackColors,
        val typography: androidx.compose.material3.Typography = androidx.compose.material3.Typography(),
        val shapes: Shapes = Shapes()
    ) {
        companion object {
            val colors: JetsnackColors
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.colors
    
            val typography: androidx.compose.material3.Typography
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.typography
    
            val shapes: Shapes
                @Composable @ReadOnlyComposable
                get() = LocalJetsnackTheme.current.shapes
    
            val styles: JetsnackStyles = JetsnackStyles
    
            val LocalJetsnackTheme: ProvidableCompositionLocal<JetsnackTheme>
                get() = LocalJetsnackThemeInstance
        }
    }
    
    val StyleScope.colors: JetsnackColors
        get() = LocalJetsnackTheme.currentValue.colors
    
    val StyleScope.typography: androidx.compose.material3.Typography
        get() = LocalJetsnackTheme.currentValue.typography
    
    val StyleScope.shapes: Shapes
        get() = LocalJetsnackTheme.currentValue.shapes
    
    internal val LocalJetsnackThemeInstance = staticCompositionLocalOf { JetsnackTheme() }
    
    @Composable
    fun JetsnackTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
        val colors = if (darkTheme) DarkJetsnackColors else LightJetsnackColors
        val theme = JetsnackTheme(colors = colors)
    
        CompositionLocalProvider(
            LocalJetsnackTheme provides theme,
        ) {
            MaterialTheme(
                typography = LocalJetsnackTheme.current.typography,
                shapes = LocalJetsnackTheme.current.shapes,
                content = content,
            )
        }
    }

  3. আপনার কম্পোজেবলের মধ্যে JetsnackStyles অ্যাক্সেস করুন:

    @Composable
    fun CustomButton(modifier: Modifier,
                     style: Style = Style,
                     text: String) {
        val interactionSource = remember { MutableInteractionSource() }
        val styleState = remember(interactionSource) { MutableStyleState(interactionSource) }
    
        // Apply style to top level container in combination with incoming style from parameter.
        Box(modifier = modifier
            .clickable(
                interactionSource = interactionSource,
                indication = null,
                enabled = true,
                role = Role.Button,
                onClick = {
    
                },
            )
            .styleable(styleState, JetsnackTheme.styles.buttonStyle, style)) {
            Text(text)
        }
    }

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