অন্যান্য বেশিরভাগ UI টুলকিটের মতোই, কম্পোজ কয়েকটি স্বতন্ত্র ধাপের মাধ্যমে একটি ফ্রেম রেন্ডার করে। উদাহরণস্বরূপ, অ্যান্ড্রয়েড ভিউ সিস্টেমের তিনটি প্রধান ধাপ রয়েছে: মেজার (পরিমাপ), লেআউট (বিন্যাস) এবং ড্রয়িং (অঙ্কন)। কম্পোজও অনেকটা একই রকম, তবে এর শুরুতে কম্পোজিশন নামে একটি গুরুত্বপূর্ণ অতিরিক্ত ধাপ রয়েছে।
Compose ডকুমেন্টেশনে Thinking in Compose and State এবং Jetpack Compose-এ কম্পোজিশন সম্পর্কে বর্ণনা করা হয়েছে।
একটি ফ্রেমের তিনটি পর্যায়
রচনার তিনটি প্রধান পর্যায় রয়েছে:
- কম্পোজিশন : কী UI দেখানো হবে। কম্পোজ কম্পোজেবল ফাংশনগুলো চালায় এবং আপনার UI-এর একটি বিবরণ তৈরি করে।
- লেআউট : UI কোথায় স্থাপন করতে হবে। এই পর্যায়ে দুটি ধাপ রয়েছে: পরিমাপ এবং স্থাপন। লেআউট ট্রি-এর প্রতিটি নোডের জন্য, লেআউট এলিমেন্টগুলো নিজেদের এবং যেকোনো চাইল্ড এলিমেন্টকে 2D স্থানাঙ্কে পরিমাপ করে ও স্থাপন করে।
- অঙ্কন : এটি কীভাবে রেন্ডার হয়। UI উপাদানগুলো একটি ক্যানভাসে, সাধারণত ডিভাইসের স্ক্রিনে, অঙ্কিত হয়।

এই পর্যায়গুলোর ক্রম সাধারণত একই থাকে, যা একটি ফ্রেম তৈরি করার জন্য কম্পোজিশন থেকে লেআউট হয়ে ড্রয়িং পর্যন্ত ডেটাকে একমুখীভাবে প্রবাহিত হতে দেয় (যা একমুখী ডেটা প্রবাহ নামেও পরিচিত)। BoxWithConstraints , LazyColumn এবং LazyRow হলো উল্লেখযোগ্য ব্যতিক্রম, যেখানে এদের চাইল্ডদের কম্পোজিশন প্যারেন্টের লেআউট পর্যায়ের উপর নির্ভর করে।
ধারণাগতভাবে, এই প্রতিটি পর্যায় প্রতিটি ফ্রেমের জন্য ঘটে; তবে পারফরম্যান্স অপ্টিমাইজ করার জন্য, কম্পোজ এমন কাজের পুনরাবৃত্তি এড়িয়ে চলে যা এই সমস্ত পর্যায়ে একই ইনপুট থেকে একই ফলাফল গণনা করবে। কম্পোজ একটি কম্পোজেবল ফাংশন চালানো এড়িয়ে যায় যদি এটি পূর্বের কোনো ফলাফল পুনরায় ব্যবহার করতে পারে, এবং কম্পোজ UI অপ্রয়োজনে পুরো ট্রি-টিকে পুনরায় লেআউট বা পুনরায় আঁকে না। UI আপডেট করার জন্য কম্পোজ শুধুমাত্র প্রয়োজনীয় ন্যূনতম পরিমাণ কাজ সম্পাদন করে। এই অপ্টিমাইজেশন সম্ভব কারণ কম্পোজ বিভিন্ন পর্যায়ের মধ্যে স্টেট রিড ট্র্যাক করে।
পর্যায়গুলো বুঝুন
এই অংশে কম্পোজেবল কন্টেন্টের জন্য কম্পোজের তিনটি পর্যায় কীভাবে সম্পাদিত হয়, তা আরও বিস্তারিতভাবে বর্ণনা করা হয়েছে।
গঠন
কম্পোজিশন পর্যায়ে, কম্পোজ রানটাইম কম্পোজেবল ফাংশনগুলো সম্পাদন করে এবং একটি ট্রি স্ট্রাকচার আউটপুট করে যা আপনার UI-কে উপস্থাপন করে। এই UI ট্রি-টি লেআউট নোড দ্বারা গঠিত, যেগুলোতে পরবর্তী পর্যায়গুলোর জন্য প্রয়োজনীয় সমস্ত তথ্য থাকে, যেমনটি নিম্নলিখিত ভিডিওতে দেখানো হয়েছে:
চিত্র ২. কম্পোজিশন পর্যায়ে তৈরি করা আপনার UI-এর প্রতিনিধিত্বকারী ট্রি।
কোড এবং UI ট্রি-এর একটি উপবিভাগ দেখতে নিম্নলিখিতের মতো:

এই উদাহরণগুলিতে, কোডের প্রতিটি কম্পোজেবল ফাংশন UI ট্রি-এর একটিমাত্র লেআউট নোডের সাথে ম্যাপ করা থাকে। আরও জটিল উদাহরণগুলিতে, কম্পোজেবলগুলিতে লজিক ও কন্ট্রোল ফ্লো থাকতে পারে এবং বিভিন্ন স্টেটের উপর ভিত্তি করে একটি ভিন্ন ট্রি তৈরি করতে পারে।
লেআউট
লেআউট পর্যায়ে, কম্পোজ ইনপুট হিসেবে কম্পোজিশন পর্যায়ে তৈরি UI ট্রি ব্যবহার করে। লেআউট নোডগুলোর সংগ্রহে 2D স্পেসে প্রতিটি নোডের আকার এবং অবস্থান নির্ধারণের জন্য প্রয়োজনীয় সমস্ত তথ্য থাকে।
চিত্র ৪। লেআউট পর্ব চলাকালীন UI ট্রি-তে প্রতিটি লেআউট নোডের পরিমাপ ও অবস্থান।
লেআউট পর্যায়ে, নিম্নলিখিত তিন-ধাপের অ্যালগরিদম ব্যবহার করে ট্রি-টি ট্র্যাভার্স করা হয়:
- চাইল্ড পরিমাপ করা : একটি নোড তার চাইল্ডদের পরিমাপ করে, যদি তাদের অস্তিত্ব থাকে।
- নিজস্ব আকার নির্ধারণ : এই পরিমাপগুলোর উপর ভিত্তি করে একটি নোড তার নিজের আকার নির্ধারণ করে।
- চাইল্ড নোডের অবস্থান : প্রতিটি চাইল্ড নোডকে একটি নোডের নিজের অবস্থানের সাপেক্ষে স্থাপন করা হয়।
এই পর্যায়ের শেষে, প্রতিটি লেআউট নোডে রয়েছে:
- একটি নির্ধারিত প্রস্থ এবং উচ্চতা
- একটি x, y স্থানাঙ্ক যেখানে এটি আঁকা উচিত
পূর্ববর্তী বিভাগ থেকে UI ট্রি-টি স্মরণ করুন:

এই ট্রি-টির জন্য অ্যালগরিদমটি নিম্নরূপে কাজ করে:
-
Rowতার অধীনস্থImageএবংColumnপরিমাপ করে। -
Imageপরিমাপ করা হয়। এর কোনো চাইল্ড নেই, তাই এটি নিজের আকার নিজেই নির্ধারণ করে এবং সেই আকারটিRowকে জানিয়ে দেয়। - এরপর
Columnপরিমাপ করা হয়। এটি প্রথমে নিজের চাইল্ডগুলোকে (দুটিTextকম্পোজেবল) পরিমাপ করে। - প্রথম
Textপরিমাপ করা হয়। এর কোনো চাইল্ড না থাকায় এটি নিজের আকার নিজেই নির্ধারণ করে এবং সেই আকারটিColumnজানিয়ে দেয়।- দ্বিতীয়
Textপরিমাপ করা হয়। এর কোনো চাইল্ড না থাকায় এটি নিজের আকার নিজেই নির্ধারণ করে এবং সেই সংখ্যাটিColumnরিপোর্ট করে।
- দ্বিতীয়
-
Columnতার নিজের আকার নির্ধারণ করতে চাইল্ড কলামগুলোর পরিমাপ ব্যবহার করে। এটি চাইল্ড কলামগুলোর সর্বোচ্চ প্রস্থ এবং তাদের উচ্চতার সমষ্টিকে কাজে লাগায়। -
Columnতার সন্তানদেরকে নিজের সাপেক্ষে উল্লম্বভাবে একটির নিচে আরেকটি স্থাপন করে। -
Rowতার নিজের আকার নির্ধারণ করতে চাইল্ডগুলোর পরিমাপ ব্যবহার করে। এটি চাইল্ডের সর্বোচ্চ উচ্চতা এবং তাদের প্রস্থের যোগফল ব্যবহার করে। এরপর এটি তার চাইল্ডগুলোকে স্থাপন করে।
লক্ষ্য করুন যে প্রতিটি নোড শুধুমাত্র একবার পরিদর্শন করা হয়েছিল। Compose রানটাইমকে সমস্ত নোড পরিমাপ ও স্থাপন করার জন্য UI ট্রি-এর মধ্য দিয়ে কেবল একবারই যেতে হয়, যা পারফরম্যান্স উন্নত করে। যখন ট্রি-তে নোডের সংখ্যা বাড়ে, তখন এটি অতিক্রম করতে ব্যয়িত সময় রৈখিক হারে বৃদ্ধি পায়। এর বিপরীতে, যদি প্রতিটি নোড একাধিকবার পরিদর্শন করা হতো, তাহলে অতিক্রম করার সময় সূচকীয় হারে বৃদ্ধি পেত।
অঙ্কন
অঙ্কন পর্যায়ে, ট্রি-টিকে আবার উপর থেকে নিচে অতিক্রম করা হয় এবং প্রতিটি নোড ক্রমানুসারে স্ক্রিনে নিজেকে অঙ্কন করে।
চিত্র ৫। অঙ্কন পর্যায়ে স্ক্রিনে পিক্সেলগুলো আঁকা হয়।
পূর্ববর্তী উদাহরণটি ব্যবহার করে, ট্রি-এর বিষয়বস্তু নিম্নলিখিত উপায়ে আঁকা হয়:
-
Rowতার নিজস্ব বিষয়বস্তু, যেমন পটভূমির রঙ, অঙ্কন করে। -
Imageনিজে থেকেই আঁকা হয়ে যায়। -
Columnনিজেই নিজের ছবি আঁকে। - প্রথম ও দ্বিতীয়
Textযথাক্রমে নিজেদেরকেই অঙ্কন করে।
চিত্র ৬. একটি UI ট্রি এবং এর অঙ্কিত উপস্থাপনা।
রাজ্য পড়ে
পূর্বে তালিকাভুক্ত পর্যায়গুলোর কোনো একটি চলাকালীন আপনি যখন একটি snapshot state value পড়েন, তখন Compose স্বয়ংক্রিয়ভাবে ট্র্যাক করে যে value পড়ার সময় এটি কী করছিল। এই ট্র্যাকিং Compose-কে স্টেটের value পরিবর্তিত হলে রিডারটিকে পুনরায় চালু করার সুযোগ দেয় এবং এটিই Compose-এ স্টেট অবজার্ভেবিলিটির ভিত্তি।
সাধারণত mutableStateOf() ব্যবহার করে স্টেট তৈরি করা হয় এবং তারপর দুটি উপায়ের একটির মাধ্যমে তা অ্যাক্সেস করা হয়: সরাসরি value প্রপার্টি অ্যাক্সেস করে, অথবা একটি কোটলিন প্রপার্টি ডেলিগেট ব্যবহার করে। আপনি "State in composables" অংশে এ সম্পর্কে আরও পড়তে পারেন। এই গাইডের জন্য, "স্টেট রিড" বলতে এই দুটি সমতুল্য অ্যাক্সেস পদ্ধতির যেকোনো একটিকে বোঝানো হয়েছে।
// State read without property delegate. val paddingState: MutableState<Dp> = remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.padding(paddingState.value) )
// State read with property delegate. var padding: Dp by remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.padding(padding) )
প্রপার্টি ডেলিগেটের অভ্যন্তরে, স্টেটের value অ্যাক্সেস এবং আপডেট করার জন্য 'গেটার' এবং 'সেটার' ফাংশন ব্যবহার করা হয়। এই গেটার এবং সেটার ফাংশনগুলো শুধুমাত্র তখনই কল করা হয় যখন আপনি প্রপার্টিটিকে একটি ভ্যালু হিসেবে রেফারেন্স করেন, এটি তৈরি করার সময় নয়, আর একারণেই পূর্বে বর্ণিত দুটি পদ্ধতি সমতুল্য।
কোডের প্রতিটি ব্লক যা রিড স্টেট পরিবর্তিত হলে পুনরায় এক্সিকিউট করা যায়, তাকে রিস্টার্ট স্কোপ বলা হয়। কম্পোজ বিভিন্ন ফেজে স্টেট value পরিবর্তন এবং রিস্টার্ট স্কোপের হিসাব রাখে।
পর্যায়ক্রমিক অবস্থা পাঠ
পূর্বে যেমন উল্লেখ করা হয়েছে, Compose-এর তিনটি প্রধান পর্যায় রয়েছে এবং Compose সেগুলোর প্রতিটির মধ্যে কোন স্টেট পড়া হচ্ছে তা ট্র্যাক করে। এর ফলে Compose আপনার UI-এর প্রতিটি প্রভাবিত এলিমেন্টের জন্য শুধুমাত্র সেই নির্দিষ্ট পর্যায়গুলোকেই অবহিত করতে পারে, যাদের কাজ সম্পাদন করা প্রয়োজন।
নিম্নলিখিত বিভাগগুলিতে প্রতিটি পর্যায় এবং এর মধ্যে কোনো অবস্থার মান পাঠ করা হলে কী ঘটে তা বর্ণনা করা হয়েছে।
পর্যায় ১: গঠন
একটি @Composable ফাংশন বা ল্যাম্বডা ব্লকের মধ্যে স্টেট রিড করা হলে তা কম্পোজিশন এবং সম্ভাব্য পরবর্তী পর্যায়গুলোকে প্রভাবিত করে। যখন স্টেটের value পরিবর্তিত হয়, তখন রিকম্পোজার সেই সমস্ত কম্পোজেবল ফাংশন পুনরায় চালানোর জন্য শিডিউল করে, যেগুলো ওই স্টেটের value রিড করে। উল্লেখ্য যে, ইনপুটগুলো পরিবর্তিত না হলে রানটাইম কিছু বা সমস্ত কম্পোজেবল ফাংশন এড়িয়ে যাওয়ার সিদ্ধান্ত নিতে পারে। আরও তথ্যের জন্য "ইনপুট পরিবর্তিত না হলে এড়িয়ে যাওয়া" দেখুন।
কম্পোজিশনের ফলাফলের উপর নির্ভর করে, কম্পোজ UI লেআউট এবং ড্রয়িং পর্যায়গুলো চালায়। যদি বিষয়বস্তু একই থাকে এবং আকার ও লেআউট অপরিবর্তিত থাকে, তবে এটি এই পর্যায়গুলো এড়িয়ে যেতে পারে।
var padding by remember { mutableStateOf(8.dp) } Text( text = "Hello", // The `padding` state is read in the composition phase // when the modifier is constructed. // Changes in `padding` will invoke recomposition. modifier = Modifier.padding(padding) )
পর্যায় ২: বিন্যাস
লেআউট পর্যায়টি দুটি ধাপ নিয়ে গঠিত: পরিমাপ এবং স্থাপন । পরিমাপ ধাপে Layout কম্পোজেবলে পাস করা মেজার ল্যাম্বডা, LayoutModifier ইন্টারফেসের MeasureScope.measure মেথড এবং অন্যান্য ফাংশনগুলো রান করে। স্থাপন ধাপে layout ফাংশনের প্লেসমেন্ট ব্লক, Modifier.offset { … } এর ল্যাম্বডা ব্লক এবং অনুরূপ ফাংশনগুলো রান করে।
এই প্রতিটি ধাপ চলাকালীন স্টেটের মান লেআউট এবং সম্ভাব্যভাবে ড্রয়িং পর্যায়কে প্রভাবিত করে। যখন স্টেটের value পরিবর্তিত হয়, কম্পোজ UI লেআউট পর্যায়টি নির্ধারণ করে। আকার বা অবস্থান পরিবর্তিত হলে এটি ড্রয়িং পর্যায়টিও চালায়।
var offsetX by remember { mutableStateOf(8.dp) } Text( text = "Hello", modifier = Modifier.offset { // The `offsetX` state is read in the placement step // of the layout phase when the offset is calculated. // Changes in `offsetX` restart the layout. IntOffset(offsetX.roundToPx(), 0) } )
পর্যায় ৩: অঙ্কন
ড্রয়িং কোডের সময় স্টেট রিড করা হলে তা ড্রয়িং পর্বকে প্রভাবিত করে। এর সাধারণ উদাহরণগুলির মধ্যে রয়েছে Canvas() , Modifier.drawBehind , এবং Modifier.drawWithContent । যখন স্টেটের value পরিবর্তিত হয়, Compose UI শুধুমাত্র ড্র পর্বটি চালায়।
var color by remember { mutableStateOf(Color.Red) } Canvas(modifier = modifier) { // The `color` state is read in the drawing phase // when the canvas is rendered. // Changes in `color` restart the drawing. drawRect(color) }
স্টেট রিড অপ্টিমাইজ করুন
যেহেতু Compose স্থানীয়ভাবে স্টেট রিড ট্র্যাকিং করে, তাই আপনি প্রতিটি স্টেটকে যথাযথ ফেজে রিড করার মাধ্যমে কাজের পরিমাণ কমিয়ে আনতে পারেন।
নিম্নলিখিত উদাহরণটি বিবেচনা করুন। এই উদাহরণটিতে একটি Image() রয়েছে যা তার চূড়ান্ত লেআউট অবস্থানকে অফসেট করতে offset মডিফায়ার ব্যবহার করে, যার ফলে ব্যবহারকারী স্ক্রোল করার সময় একটি প্যারালাক্স প্রভাব তৈরি হয়।
Box { val listState = rememberLazyListState() Image( // ... // Non-optimal implementation! Modifier.offset( with(LocalDensity.current) { // State read of firstVisibleItemScrollOffset in composition (listState.firstVisibleItemScrollOffset / 2).toDp() } ) ) LazyColumn(state = listState) { // ... } }
এই কোডটি কাজ করে, কিন্তু এর পারফরম্যান্স আশানুরূপ নয়। যেভাবে কোডটি লেখা হয়েছে, তাতে এটি firstVisibleItemScrollOffset স্টেটের value পড়ে এবং Modifier.offset(offset: Dp) ফাংশনে তা পাস করে। ব্যবহারকারী যখন স্ক্রল করেন, তখন firstVisibleItemScrollOffset এর value পরিবর্তিত হয়। আপনারা যেমন শিখেছেন, Compose যেকোনো স্টেট রিড ট্র্যাক করে রাখে, যাতে এটি রিডিং কোডটি পুনরায় চালু (পুনরায় আহ্বান) করতে পারে, যা এই উদাহরণে হলো Box এর কন্টেন্ট।
এটি কম্পোজিশন পর্যায়ে কোনো স্টেট পড়ার একটি উদাহরণ। এটি অগত্যা কোনো খারাপ বিষয় নয়, এবং প্রকৃতপক্ষে এটিই রিকম্পোজিশনের ভিত্তি, যা ডেটা পরিবর্তনের মাধ্যমে নতুন UI তৈরি করতে সাহায্য করে।
মূল বিষয়: এই উদাহরণটি সর্বোত্তম নয়, কারণ প্রতিটি স্ক্রল ইভেন্টের ফলে সম্পূর্ণ কম্পোজেবল কন্টেন্টটি পুনরায় মূল্যায়ন, পরিমাপ, লেআউট এবং অবশেষে অঙ্কন করা হয়। আপনি প্রতিটি স্ক্রলে কম্পোজ পর্যায়টি ট্রিগার করছেন, যদিও প্রদর্শিত কন্টেন্টের কোনো পরিবর্তন হয়নি, শুধু তার অবস্থান পরিবর্তিত হয়েছে। আপনি স্টেট রিডকে এমনভাবে অপ্টিমাইজ করতে পারেন যাতে এটি শুধুমাত্র লেআউট পর্যায়টিকে পুনরায় ট্রিগার করে।
ল্যাম্বডা দিয়ে অফসেট
অফসেট মডিফায়ারের আরেকটি সংস্করণ রয়েছে: Modifier.offset(offset: Density.() -> IntOffset) ।
এই ভার্সনটি একটি ল্যাম্বডা প্যারামিটার গ্রহণ করে, যার ফলে প্রাপ্ত অফসেটটি ল্যাম্বডা ব্লক দ্বারা রিটার্ন করা হয়। এটি ব্যবহার করার জন্য কোডটি আপডেট করুন:
Box { val listState = rememberLazyListState() Image( // ... Modifier.offset { // State read of firstVisibleItemScrollOffset in Layout IntOffset(x = 0, y = listState.firstVisibleItemScrollOffset / 2) } ) LazyColumn(state = listState) { // ... } }
তাহলে এটি কেন আরও বেশি পারফরম্যান্ট? আপনি মডিফায়ারে যে ল্যাম্বডা ব্লকটি দেন, তা লেআউট ফেজের সময় (বিশেষত, লেআউট ফেজের প্লেসমেন্ট স্টেপের সময়) কল করা হয়, যার মানে হলো কম্পোজিশনের সময় firstVisibleItemScrollOffset স্টেটটি আর পড়া হয় না। যেহেতু কম্পোজ ট্র্যাক করে কখন স্টেট পড়া হচ্ছে, এই পরিবর্তনের ফলে যদি firstVisibleItemScrollOffset এর value পরিবর্তিত হয়, তবে কম্পোজকে শুধুমাত্র লেআউট এবং ড্রয়িং ফেজগুলো পুনরায় শুরু করতে হয়।
অবশ্যই, কম্পোজিশন পর্যায়ে স্টেটগুলো পড়া প্রায়শই অপরিহার্য হয়ে পড়ে। তা সত্ত্বেও, এমন কিছু ক্ষেত্র রয়েছে যেখানে স্টেট পরিবর্তনগুলো ফিল্টার করার মাধ্যমে রিকম্পোজিশনের সংখ্যা কমানো যায়। এ বিষয়ে আরও তথ্যের জন্য, derivedStateOf : convert one or multiple state objects into another state” দেখুন।
পুনর্গঠন লুপ (চক্রীয় দশা নির্ভরতা)
এই নির্দেশিকায় আগে উল্লেখ করা হয়েছে যে কম্পোজের পর্যায়গুলো সর্বদা একই ক্রমে কার্যকর হয় এবং একই ফ্রেমে থাকাকালীন পেছনে যাওয়ার কোনো উপায় নেই। তবে, এটি অ্যাপগুলোকে বিভিন্ন ফ্রেম জুড়ে কম্পোজিশন লুপে প্রবেশ করতে বাধা দেয় না। এই উদাহরণটি বিবেচনা করুন:
Box { var imageHeightPx by remember { mutableIntStateOf(0) } Image( painter = painterResource(R.drawable.rectangle), contentDescription = "I'm above the text", modifier = Modifier .fillMaxWidth() .onSizeChanged { size -> // Don't do this imageHeightPx = size.height } ) Text( text = "I'm below the image", modifier = Modifier.padding( top = with(LocalDensity.current) { imageHeightPx.toDp() } ) ) }
এই উদাহরণটিতে একটি উল্লম্ব কলাম দেখানো হয়েছে, যার শীর্ষে ছবিটি এবং তার নিচে লেখা রয়েছে। এটি ছবির চূড়ান্ত আকার পাওয়ার জন্য Modifier.onSizeChanged() ব্যবহার করে এবং তারপর লেখাটিকে নিচে নামানোর জন্য Modifier.padding() প্রয়োগ করে। Px থেকে ডেসিবেলে Dp এই অস্বাভাবিক রূপান্তরটিই ইঙ্গিত দেয় যে কোডটিতে কোনো সমস্যা আছে।
এই উদাহরণটির সমস্যা হলো, কোডটি একটিমাত্র ফ্রেমের মধ্যে "চূড়ান্ত" লেআউটে পৌঁছায় না। কোডটি একাধিক ফ্রেমের ওপর নির্ভর করে, যা অপ্রয়োজনীয় কাজ সম্পাদন করে এবং এর ফলে ব্যবহারকারীর জন্য স্ক্রিনে UI এলোমেলোভাবে লাফাতে থাকে।
প্রথম ফ্রেমের গঠন
প্রথম ফ্রেমের কম্পোজিশন পর্যায়ে, imageHeightPx এর প্রাথমিক মান 0 থাকে। ফলে, কোডটি টেক্সটকে Modifier.padding(top = 0) প্যাডিং প্রদান করে। পরবর্তী লেআউট পর্যায়ে onSizeChanged মডিফায়ারের কলব্যাকটি কল করা হয়, যা imageHeightPx ইমেজের আসল উচ্চতায় আপডেট করে। এরপর Compose পরবর্তী ফ্রেমের জন্য একটি রিকম্পোজিশন শিডিউল করে। কিন্তু, বর্তমান ড্রয়িং পর্যায়ে, টেক্সটটি 0 প্যাডিং সহ রেন্ডার হয়, কারণ আপডেট করা imageHeightPx মানটি তখনও প্রতিফলিত হয়নি।
দ্বিতীয় ফ্রেমের রচনা
imageHeightPx এর মানের পরিবর্তনের ফলে `Compose` দ্বিতীয় ফ্রেমটি শুরু করে। এই ফ্রেমের কম্পোজিশন পর্যায়ে, Box কন্টেন্ট ব্লকের ভেতর থেকে স্টেটটি পড়া হয়। এখন টেক্সটটিতে এমন একটি প্যাডিং যোগ করা হয় যা ইমেজের উচ্চতার সাথে সঠিকভাবে মিলে যায়। লেআউট পর্যায়ে, imageHeightPx আবার সেট করা হয়; কিন্তু, মানটি অপরিবর্তিত থাকায় আর কোনো পুনর্গঠন নির্ধারিত হয় না।
এই উদাহরণটি কৃত্রিম মনে হতে পারে, কিন্তু এই সাধারণ ধরনটি সম্পর্কে সতর্ক থাকুন:
-
Modifier.onSizeChanged(),onGloballyPositioned(), অথবা অন্য কোনো লেআউট অপারেশন - কিছু অবস্থা আপডেট করুন
- সেই স্টেটটিকে লেআউট মডিফায়ারের (
padding(),height(), বা অনুরূপ) ইনপুট হিসেবে ব্যবহার করুন। - সম্ভাব্য পুনরাবৃত্তি
পূর্ববর্তী নমুনাটির সমাধান হলো সঠিক লেআউট প্রিমিটিভ ব্যবহার করা। পূর্ববর্তী উদাহরণটি একটি Column() দিয়ে প্রয়োগ করা যেতে পারে, কিন্তু আপনার কাছে আরও জটিল কোনো উদাহরণ থাকতে পারে যার জন্য কাস্টম কিছুর প্রয়োজন হবে, যার জন্য একটি কাস্টম লেআউট লিখতে হবে। আরও তথ্যের জন্য কাস্টম লেআউট গাইডটি দেখুন।
এখানকার সাধারণ নীতিটি হলো একাধিক UI এলিমেন্টের জন্য তথ্যের একটি একক উৎস থাকা, যেগুলোকে একে অপরের সাপেক্ষে পরিমাপ ও স্থাপন করা উচিত। একটি উপযুক্ত লেআউট প্রিমিটিভ ব্যবহার করা বা একটি কাস্টম লেআউট তৈরি করার অর্থ হলো, ন্যূনতম শেয়ার্ড প্যারেন্টটিই তথ্যের উৎস হিসেবে কাজ করে, যা একাধিক এলিমেন্টের মধ্যে সম্পর্ক সমন্বয় করতে পারে। একটি ডাইনামিক স্টেট যুক্ত করলে এই নীতিটি লঙ্ঘিত হয়।
{% হুবহু %}আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলেও লিঙ্কের লেখা প্রদর্শিত হয়।
- স্টেট এবং জেটপ্যাক কম্পোজ
- তালিকা এবং গ্রিড
- জেটপ্যাক কম্পোজের জন্য কোটলিন