Compose-এ UI অপরিবর্তনীয়—একবার আঁকা হয়ে গেলে এটিকে আপডেট করার কোনো উপায় নেই। আপনি যা নিয়ন্ত্রণ করতে পারেন তা হলো আপনার UI-এর স্টেট। প্রতিবার UI-এর স্টেট পরিবর্তিত হলে, Compose UI ট্রি-এর পরিবর্তিত অংশগুলো পুনরায় তৈরি করে । Composable-গুলো স্টেট গ্রহণ করতে এবং ইভেন্ট প্রকাশ করতে পারে—উদাহরণস্বরূপ, একটি TextField একটি ভ্যালু গ্রহণ করে এবং onValueChange নামে একটি কলব্যাক প্রকাশ করে, যা কলব্যাক হ্যান্ডলারকে ভ্যালুটি পরিবর্তন করার জন্য অনুরোধ করে।
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
যেহেতু কম্পোজেবলগুলো স্টেট গ্রহণ করে এবং ইভেন্ট প্রকাশ করে, তাই একমুখী ডেটা ফ্লো প্যাটার্নটি জেটপ্যাক কম্পোজের সাথে ভালোভাবে খাপ খায়। এই নির্দেশিকাটি কম্পোজে কীভাবে একমুখী ডেটা ফ্লো প্যাটার্ন প্রয়োগ করতে হয়, কীভাবে ইভেন্ট এবং স্টেট হোল্ডার ব্যবহার করতে হয় এবং কম্পোজে ভিউমডেল নিয়ে কীভাবে কাজ করতে হয়, তার উপর আলোকপাত করে।
একমুখী ডেটা প্রবাহ
একমুখী ডেটা প্রবাহ (UDF) হলো একটি ডিজাইন প্যাটার্ন যেখানে স্টেট নিচের দিকে এবং ইভেন্টগুলো উপরের দিকে প্রবাহিত হয়। একমুখী ডেটা প্রবাহ অনুসরণ করে, আপনি আপনার অ্যাপের স্টেট সংরক্ষণ ও পরিবর্তনকারী অংশগুলো থেকে UI-তে স্টেট প্রদর্শনকারী কম্পোজেবলগুলোকে বিচ্ছিন্ন করতে পারেন।
একমুখী ডেটা প্রবাহ ব্যবহারকারী একটি অ্যাপের UI আপডেট লুপটি দেখতে এইরকম হয়:
- ইভেন্ট : UI-এর কোনো অংশ একটি ইভেন্ট তৈরি করে এবং সেটিকে উপরের স্তরে পাঠায়, যেমন একটি বাটন ক্লিকের ঘটনা হ্যান্ডেল করার জন্য ViewModel-এ পাঠানো; অথবা আপনার অ্যাপের অন্যান্য স্তর থেকে একটি ইভেন্ট পাঠানো হয়, যেমন ব্যবহারকারীর সেশনের মেয়াদ শেষ হয়ে গেছে তা নির্দেশ করা।
- অবস্থা হালনাগাদ করুন : একটি ইভেন্ট হ্যান্ডলার অবস্থা পরিবর্তন করতে পারে।
- অবস্থা প্রদর্শন : অবস্থা ধারক অবস্থাটি প্রেরণ করেন এবং UI তা প্রদর্শন করে।

Jetpack Compose ব্যবহার করার সময় এই পদ্ধতি অনুসরণ করলে বেশ কিছু সুবিধা পাওয়া যায়:
- পরীক্ষাযোগ্যতা : যে UI স্টেট প্রদর্শন করে, তা থেকে স্টেটকে আলাদা করলে উভয়কে পৃথকভাবে পরীক্ষা করা সহজ হয়।
- স্টেট এনক্যাপসুলেশন : যেহেতু স্টেট শুধুমাত্র একটি জায়গাতেই আপডেট করা যায় এবং একটি কম্পোজেবলের স্টেটের জন্য তথ্যের উৎস একটিই থাকে, তাই অসামঞ্জস্যপূর্ণ স্টেটের কারণে বাগ তৈরি হওয়ার সম্ভাবনা কম থাকে।
- UI সামঞ্জস্যতা :
StateFlowবাLiveDataমতো পর্যবেক্ষণযোগ্য স্টেট হোল্ডার ব্যবহারের মাধ্যমে সমস্ত স্টেট আপডেট তাৎক্ষণিকভাবে UI-তে প্রতিফলিত হয়।
Jetpack Compose-এ একমুখী ডেটা প্রবাহ
কম্পোজেবলগুলো স্টেট এবং ইভেন্টের উপর ভিত্তি করে কাজ করে। উদাহরণস্বরূপ, একটি TextField তখনই আপডেট হয় যখন এর value প্যারামিটারটি আপডেট করা হয় এবং এটি একটি onValueChange কলব্যাক প্রকাশ করে—এমন একটি ইভেন্ট যা ভ্যালুটিকে একটি নতুন ভ্যালুতে পরিবর্তন করার অনুরোধ করে। কম্পোজ State অবজেক্টকে একটি ভ্যালু হোল্ডার হিসেবে সংজ্ঞায়িত করে, এবং স্টেট ভ্যালুর পরিবর্তন একটি রিকম্পোজিশন ট্রিগার করে। ভ্যালুটি কতক্ষণের জন্য মনে রাখতে হবে তার উপর নির্ভর করে আপনি স্টেটটিকে remember { mutableStateOf(value) } অথবা rememberSaveable { mutableStateOf(value) এর মধ্যে রাখতে পারেন।
TextField কম্পোজেবলের ভ্যালুর টাইপ হলো String , তাই এটি যেকোনো জায়গা থেকে আসতে পারে—হার্ডকোডেড ভ্যালু থেকে, ViewModel থেকে, অথবা প্যারেন্ট কম্পোজেবল থেকে পাস করা হয়ে। এটিকে কোনো State অবজেক্টে রাখার প্রয়োজন নেই, কিন্তু যখন onValueChange কল করা হয়, তখন ভ্যালুটি আপডেট করতে হবে।
কম্পোজেবল প্যারামিটার সংজ্ঞায়িত করুন
একটি কম্পোজেবলের স্টেট প্যারামিটার নির্ধারণ করার সময়, নিম্নলিখিত প্রশ্নগুলো মনে রাখবেন:
- কম্পোজেবলটি কতটা পুনঃব্যবহারযোগ্য বা নমনীয়?
- স্টেট প্যারামিটারগুলো এই কম্পোজেবলটির পারফরম্যান্সকে কীভাবে প্রভাবিত করে?
ডিকাপলিং এবং পুনঃব্যবহারকে উৎসাহিত করার জন্য, প্রতিটি কম্পোজেবলে যথাসম্ভব সর্বনিম্ন পরিমাণ তথ্য থাকা উচিত। উদাহরণস্বরূপ, কোনো সংবাদ নিবন্ধের শিরোনাম রাখার জন্য একটি কম্পোজেবল তৈরি করার সময়, সম্পূর্ণ সংবাদ নিবন্ধটির পরিবর্তে শুধুমাত্র প্রদর্শনের জন্য প্রয়োজনীয় তথ্যটুকু পাস করা শ্রেয়।
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
কখনও কখনও, স্বতন্ত্র প্যারামিটার ব্যবহার করলে পারফরম্যান্সও উন্নত হয়—উদাহরণস্বরূপ, যদি News শুধু title এবং subtitle চেয়েও বেশি তথ্য থাকে, তাহলে যখনই Header(news) এ News এর একটি নতুন ইনস্ট্যান্স পাস করা হয়, তখন কম্পোজেবলটি পুনরায় কম্পোজ হয়ে যায়, এমনকি যদি title এবং subtitle পরিবর্তিত না-ও হয়।
আপনি কতগুলো প্যারামিটার পাস করছেন, তা সাবধানে বিবেচনা করুন। একটি ফাংশনে অতিরিক্ত প্যারামিটার থাকলে তার ব্যবহারযোগ্যতা কমে যায়, তাই এক্ষেত্রে সেগুলোকে একটি ক্লাসের মধ্যে একত্রিত করা শ্রেয়।
কম্পোজে ইভেন্ট
আপনার অ্যাপের প্রতিটি ইনপুটকে একটি ইভেন্ট হিসেবে উপস্থাপন করা উচিত: যেমন ট্যাপ, টেক্সট পরিবর্তন, এমনকি টাইমার বা অন্যান্য আপডেট। যেহেতু এই ইভেন্টগুলো আপনার UI-এর অবস্থা পরিবর্তন করে, তাই ViewModel উচিত সেগুলোকে হ্যান্ডেল করা এবং UI-এর অবস্থা আপডেট করা।
ইভেন্ট হ্যান্ডলারের বাইরে UI লেয়ারের স্টেট কখনোই পরিবর্তন করা উচিত নয়, কারণ এটি আপনার অ্যাপ্লিকেশনে অসঙ্গতি এবং বাগ তৈরি করতে পারে।
স্টেট এবং ইভেন্ট হ্যান্ডলার ল্যাম্বডার জন্য অপরিবর্তনীয় মান পাস করা শ্রেয়। এই পদ্ধতির নিম্নলিখিত সুবিধাগুলো রয়েছে:
- আপনি পুনঃব্যবহারযোগ্যতা উন্নত করেন।
- আপনি যাচাই করুন যে আপনার UI সরাসরি স্টেটের মান পরিবর্তন করে না।
- আপনি কনকারেন্সি সমস্যা এড়াতে পারেন, কারণ আপনি নিশ্চিত করেন যে স্টেটটি অন্য কোনো থ্রেড থেকে পরিবর্তিত হচ্ছে না।
- প্রায়শই, আপনি কোডের জটিলতা হ্রাস করেন।
উদাহরণস্বরূপ, একটি কম্পোজেবল যা প্যারামিটার হিসেবে একটি String এবং একটি ল্যাম্বডা গ্রহণ করে, তা অনেক প্রেক্ষাপট থেকে কল করা যেতে পারে এবং এটি অত্যন্ত পুনঃব্যবহারযোগ্য। ধরুন, আপনার অ্যাপের উপরের অ্যাপ বারটি সর্বদা টেক্সট প্রদর্শন করে এবং এতে একটি ব্যাক বাটন আছে। আপনি একটি আরও জেনেরিক MyAppTopAppBar কম্পোজেবল সংজ্ঞায়িত করতে পারেন যা প্যারামিটার হিসেবে টেক্সট এবং ব্যাক বাটন হ্যান্ডলার গ্রহণ করে:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ভিউমডেল, স্টেট এবং ইভেন্ট: একটি উদাহরণ
ViewModel এবং mutableStateOf ব্যবহার করে, আপনি আপনার অ্যাপে একমুখী ডেটা প্রবাহও চালু করতে পারেন, যদি নিম্নলিখিতগুলির মধ্যে একটি সত্য হয়:
- আপনার UI-এর অবস্থা
StateFlowবাLiveDataমতো অবজার্ভেবল স্টেট হোল্ডার ব্যবহার করে প্রকাশ করা হয়। -
ViewModelআপনার অ্যাপের UI বা অন্যান্য লেয়ার থেকে আসা ইভেন্টগুলো পরিচালনা করে এবং সেই ইভেন্টগুলোর উপর ভিত্তি করে স্টেট হোল্ডারকে আপডেট করে।
উদাহরণস্বরূপ, একটি সাইন-ইন স্ক্রিন তৈরি করার সময়, ' সাইন ইন' বোতামে ট্যাপ করলে আপনার অ্যাপে একটি প্রোগ্রেস স্পিনার এবং একটি নেটওয়ার্ক কল দেখানো উচিত। লগইন সফল হলে, আপনার অ্যাপ একটি ভিন্ন স্ক্রিনে চলে যাবে; কোনো ত্রুটির ক্ষেত্রে অ্যাপটি একটি স্নাকবার দেখাবে। স্ক্রিনের অবস্থা এবং ইভেন্টটি আপনি যেভাবে মডেল করবেন তা নিচে দেওয়া হলো:
স্ক্রিনের চারটি অবস্থা রয়েছে:
- সাইন আউট করা হয়েছে : যখন ব্যবহারকারী এখনও সাইন ইন করেননি।
- প্রক্রিয়াধীন : যখন আপনার অ্যাপ একটি নেটওয়ার্ক কল করার মাধ্যমে ব্যবহারকারীকে সাইন ইন করার চেষ্টা করে।
- ত্রুটি : সাইন ইন করার সময় কোনো ত্রুটি ঘটলে।
- সাইন ইন করা আছে : যখন ব্যবহারকারী সাইন ইন করা থাকেন।
আপনি এই স্টেটগুলোকে একটি সিলড ক্লাস হিসেবে মডেল করতে পারেন। ViewModel একটি State হিসেবে স্টেটটি প্রকাশ করে, প্রাথমিক স্টেট সেট করে এবং প্রয়োজন অনুযায়ী স্টেট আপডেট করে। ViewModel একটি onSignIn() মেথড প্রকাশ করার মাধ্যমে সাইন-ইন ইভেন্টটিও পরিচালনা করে।
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
mutableStateOf API-এর পাশাপাশি, Compose-এ LiveData , Flow , এবং Observable এর জন্য এক্সটেনশন রয়েছে, যা লিসেনার হিসেবে রেজিস্টার করতে এবং ভ্যালুটিকে স্টেট হিসেবে উপস্থাপন করতে ব্যবহৃত হয়।
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
আরও জানুন
Jetpack Compose-এর আর্কিটেকচার সম্পর্কে আরও জানতে, নিম্নলিখিত রিসোর্সগুলো দেখুন:
নমুনা
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলেও লিঙ্কের লেখা প্রদর্শিত হয়।
- স্টেট এবং জেটপ্যাক কম্পোজ
- কম্পোজে UI অবস্থা সংরক্ষণ করুন
- ব্যবহারকারীর ইনপুট পরিচালনা করুন