যখন আমি Jetpack Compose সম্পর্কে আমার শেষ আর্টিকেল লিখেছিলাম, তখন আমি বলেছিলাম যে Jetpack Compose কিছু (আমার মতামতে) বেসিক কম্পোনেন্ট মিস করছে, এবং 그중 একটি হলো টুলটিপ।
সেটি সময়ে, টুলটিপ প্রদর্শনের জন্য কোনো বিল্ট-ইন কম্পোজেবল ছিল না এবং অনলাইনে বেশ কিছু অল্টারনেটিভ সমাধান ঘুরছিল। সেই সমাধানগুলোর সমস্যা ছিল যে Jetpack Compose নতুন ভার্সন রিলিজ করলে, সেই সমাধানগুলো ভেঙে যেতে পারে। তাই সেটি আইডিয়াল নয় ছিল এবং কমিউনিটি ভবিষ্যতে টুলটিপের জন্য সাপোর্ট যোগ হওয়ার আশা করছিল।
আমি খুশি হতে পারছি যে Compose Material 3-এর 1.1.0 ভার্সন থেকে, আমাদের এখন বিল্ট-ইন টুলটিপ সাপোর্ট আছে। 👏
যদিও এটাই খুব ভালো খবর, সেই ভার্সন রিলিজ হওয়ার পর এক বছরের বেশি বीत चुका। এবং পরবর্তী ভার্সনগুলোতে, টুলটিপ সম্পর্কিত API ড্রাস্টিক্যালি পরিবর্তন হয়েছে।
আপনি যদি চেঞ্জলগ দেখেন, আপনি পাবেন পাবলিক এবং ইন্টারনাল API গুলো কীভাবে পরিবর্তন হয়েছে। তাই মনে রাখবেন, আপনি এই আর্টিকেল পড়ার সময়, জিনিসগুলো আরো পরিবর্তন হতে পারে কারণ টুলটিপ 관련된 সবকিছু এখনো ExperimentalMaterial3Api::class অ্যানোটেশন দিয়ে চিহ্নিত।
❗️ এই আর্টিকেলে ব্যবহৃত material 3 এর ভার্সন 1.2.1, যা March 6th, 2024-এ রিলিজ হয়েছিল।
এখন আমাদের দুটি ধরণের টুলটিপের সাপোর্ট আছে:
Plain tooltip
Rich media tooltip
Plain Tooltip
আপনি প্রথম টাইপটি ব্যবহার করতে পারেন একটি আইকন বাটন সম্পর্কে ইনফরমেশন দিতে যা অন্যথায় স্পষ্ট নয়। উদাহরণস্বরূপ, আপনি একটি plain tooltip ব্যবহার করতে পারেন ব্যবহারকারীকে ইঙ্গিত দিতে যে আইকন বাটনটি কী প্রতীক করছে।

আপনার অ্যাপ্লিকেশনে টুলটিপ যোগ করতে, আপনি TooltipBox কম্পোজেবল ব্যবহার করবেন। এই কম্পোজেবলটি বেশ কিছু আর্গুমেন্ট নেয়:
fun TooltipBox( positionProvider: PopupPositionProvider, tooltip: @Composable TooltipScope.() -> Unit, state: TooltipState, modifier: Modifier = Modifier, focusable: Boolean = true, enableUserInput: Boolean = true, content: @Composable () -> Unit, )
এগুলোর মধ্যে কিছু আপনার পরিচিত লাগতে পারে যদি আপনি আগে Composables ব্যবহার করে থাকেন। আমি যেগুলোর স্পেসিফিক ইউজ কেস আছে সেগুলো হাইলাইট করছি:
positionProvider - PopupPositionProvider টাইপের, এবং টুলটিপের পজিশন ক্যালকুলেট করার জন্য ব্যবহৃত হয়।
tooltip - আপনি এখানে ডিজাইন করতে পারেন টুলটিপ কীভাবে দেখাবে।
state - এটি একটি নির্দিষ্ট Tooltip ইনস্ট্যান্সের সাথে যুক্ত স্টেট ধারণ করে। এটি show/dismiss টুলটিপের মতো মেথড এক্সপোজ করে এবং একটি ইনস্ট্যান্স ইনস্ট্যানশিয়েট করার সময়, আপনি ডিক্লেয়ার করতে পারেন টুলটিপটি persistent হবে কিনা (অর্থাৎ এটি স্ক্রিনে প্রদর্শিত থাকবে যতক্ষণ না ব্যবহারকারী টুলটিপের বাইরে ক্লিক করে)।
content - এটি সেটি UI যা টুলটিপ উপরে/নিচে দেখাবে।
এখানে একটি উদাহরণ দেওয়া হলো সব প্রাসঙ্গিক আর্গুমেন্ট বসিয়ে একটি BasicTooltipBox ইনস্ট্যানশিয়েট করার:
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class) @Composable fun BasicTooltip() { val tooltipPosition = TooltipDefaults.rememberPlainTooltipPositionProvider() val tooltipState = rememberBasicTooltipState(isPersistent = false) BasicTooltipBox(positionProvider = tooltipPosition, tooltip = { Text("Hello World") } , state = tooltipState) { IconButton(onClick = { }) { Icon(imageVector = Icons.Filled.Favorite, contentDescription = "Your icon's description") } } }

Jetpack Compose-এ TooltipDefaults নামে একটি বিল্ট-ইন ক্লাস আছে। আপনি এই ক্লাসটি ব্যবহার করতে পারেন টুলটিপ বক্স তৈরি করার আর্গুমেন্টগুলো ইনস্ট্যানশিয়েট করতে। উদাহরণস্বরূপ, আপনি TooltipDefaults.rememberPlainTooltipPositionProvider ব্যবহার করতে পারেন টুলটিপকে এনকর এলিমেন্টের সাপেক্ষে সঠিকভাবে পজিশন করতে।
Rich Tooltip
একটি rich media tooltip plain tooltip-এর চেয়ে বেশি জায়গা নেয় এবং একটি আইকন বাটনের কার্যকারিতা সম্পর্কে আরও কনটেক্সট দিতে ব্যবহার করা হয়। টুলটিপ দেখাওয়ার সময়, আপনি সেখানে বাটন এবং লিংক যোগ করতে পারেন আরও ব্যাখ্যা বা ডেফিনিশন দিতে।
এটি plain tooltip-এর মতোই TooltipBox-এর ভেতরে ইনস্ট্যানশিয়েট হয়, কিন্তু আপনি RichTooltip কম্পোজেবল ব্যবহার করবেন।
TooltipBox(positionProvider = tooltipPosition, tooltip = { RichTooltip( title = { Text("RichTooltip") }, caretSize = caretSize, action = { TextButton(onClick = { scope.launch { tooltipState.dismiss() tooltipState.onDispose() } }) { Text("Dismiss") } } ) { Text("This is where a description would go.") } }, state = tooltipState) { IconButton(onClick = { /* Icon button's click event */ }) { Icon(imageVector = tooltipIcon, contentDescription = "Your icon's description", tint = iconColor) } }
Rich tooltip সম্পর্কে কিছু কথা লক্ষ্য করুন:
একটি Rich tooltip caret-এর জন্য সাপোর্ট করে।
আপনি টুলটিপে একটি অ্যাকশন (যা হচ্ছে একটি বাটন) যোগ করতে পারেন ব্যবহারকারীকে আরও ইনফরমেশন পাওয়ার অপশন দিতে।
আপনি টুলটিপ ডিসমিস করার লজিক যোগ করতে পারেন।


Edge Cases
যখন আপনি আপনার tooltip state কে persistent চিহ্নিত করেন, এর মানে হলো একবার ব্যবহারকারী যে UI-র সাথে ইন্টারঅ্যাক্ট করবে যা আপনার টুলটিপ দেখাবে, সেটি দৃশ্যমান থাকবে যতক্ষণ না ব্যবহারকারী স্ক্রিনের যেকোনো অন্য জায়গায় প্রেস করে।
আপনি যদি উপরের Rich tooltip উদাহরণটি দেখেন, লক্ষ্য করতে পারেন যে আমরা একটি বাটন যোগ করেছি টুলটিপ ডিসমিস করার জন্য একবার ক্লিক হলে।
একটি সমস্যা হয় একবার ব্যবহারকারী সেটি বাটনটি প্রেস করলে। যেহেতু ডিসমিস অ্যাকশন টুলটিপের ওপর পারফর্ম হয়, যদি একজন ব্যবহারকারী সেই UI আইটেমে যা এই টুলটিপ আহ্বান করে সেটিতে আবার লং প্রেস করতে চায়, টুলটিপ আবার দেখাবে না। এর মানে হলো টুলটিপের স্টেট persistent থাকে এর dismissed হওয়া পর্যন্ত। তাহলে আমরা এটিকে কীভাবে সমাধান করব?

টুলটিপের স্টেট "রিসেট" করতে, আমাদের টুলটিপ স্টেটের মাধ্যমে এক্সপোজ করা onDispose মেথড কল করতে হবে। একবার আমরা সেটি করলে, টুলটিপ স্টেট রিসেট হয়ে যায় এবং টুলটিপ আবার দেখাবে যখন ব্যবহারকারী UI আইটেমে লং প্রেস করবে।
@OptIn(ExperimentalMaterial3Api::class) @Composable fun RichTooltip() { val tooltipPosition = TooltipDefaults.rememberRichTooltipPositionProvider() val tooltipState = rememberTooltipState(isPersistent = true) val scope = rememberCoroutineScope() TooltipBox(positionProvider = tooltipPosition, tooltip = { RichTooltip( title = { Text("RichTooltip") }, caretSize = TooltipDefaults.caretSize, action = { TextButton(onClick = { scope.launch { tooltipState.dismiss() tooltipState.onDispose() /// <---- HERE } }) { Text("Dismiss") } } ) { } }, state = tooltipState) { IconButton(onClick = { }) { Icon(imageVector = Icons.Filled.Call, contentDescription = "Your icon's description") } } }

একটি অন্য সিনেরিও যেখানে টুলটিপ স্টেট রিসেট হয় না: যদি বদলে ব্যবহারকারীর অ্যাকশন অনুযায়ী আমরা নিজেরা ডিসমিস মেথড কল করি, ব্যবহারকারী টুলটিপের বাইরে ক্লিক করে, যা এটি ডিসমিস করে। এটি পিছনে ডিসমিস মেথড কল করে এবং টুলটিপ স্টেট dismissed-এ সেট হয়। UI এলিমেন্টে লং প্রেস করে আমাদের টুলটিপ আবার দেখতে চাইলে কিছুই হবে না।

আমাদের লজিক যা টুলটিপের onDispose মেথড কল করে সেটি ট্রিগার হয় না, তাহলে আমরা টুলটিপের স্টেট কীভাবে রিসেট করব?
একসময়, আমি এটিকে সমাধান করতে পারিনি। এটি টুলটিপের MutatorMutex-এর সাথে সম্পর্কিত হতে পারে। সম্ভবত আগামী রিলিজগুলোতে এর জন্য একটি API আসবে। আমি লক্ষ্য করেছি যে যদি স্ক্রিনে অন্যান্য টুলটিপ থাকে এবং সেগুলো প্রেস করা হয়, তবে এটি পূর্বে ক্লিক করা টুলটিপটি রিসেট করে।

আপনি এখানে ফিচার করা কোডটি দেখতে চাইলে, এই GitHub রিপোজিটরি যেতে পারেন।
আপনি যদি একটি অ্যাপ্লিকেশনে টুলটিপ দেখতে চান, তবে এখানে চেক আউট করতে পারেন।
রেফারেন্স
Material3 Tooltip Overview
Tooltip Defaults
Tooltip Source Code
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started