নেভিগেশন 3 দৃশ্যের মাধ্যমে আপনার অ্যাপের UI প্রবাহ পরিচালনা করার জন্য একটি শক্তিশালী এবং নমনীয় সিস্টেম প্রবর্তন করে। দৃশ্যগুলি আপনাকে অত্যন্ত কাস্টমাইজড লেআউট তৈরি করতে, বিভিন্ন স্ক্রিনের আকারের সাথে খাপ খাইয়ে নিতে এবং জটিল মাল্টি-পেন অভিজ্ঞতাগুলিকে নির্বিঘ্নে পরিচালনা করতে দেয়৷
দৃশ্যগুলো বুঝুন
নেভিগেশন 3-এ, একটি Scene
হল মৌলিক একক যা এক বা একাধিক NavEntry
দৃষ্টান্ত রেন্ডার করে। একটি Scene
একটি স্বতন্ত্র ভিজ্যুয়াল অবস্থা বা আপনার UI এর বিভাগ হিসাবে ভাবুন যা আপনার পিছনের স্ট্যাক থেকে সামগ্রীর প্রদর্শন ধারণ করতে এবং পরিচালনা করতে পারে।
প্রতিটি Scene
উদাহরণ স্বতন্ত্রভাবে তার key
এবং Scene
শ্রেণি দ্বারা চিহ্নিত করা হয়। এই অনন্য শনাক্তকারীটি অত্যন্ত গুরুত্বপূর্ণ কারণ এটি Scene
পরিবর্তনের সময় শীর্ষ-স্তরের অ্যানিমেশন চালায়।
Scene
ইন্টারফেসের নিম্নলিখিত বৈশিষ্ট্য রয়েছে:
-
key: Any
: এই নির্দিষ্টScene
উদাহরণের জন্য একটি অনন্য শনাক্তকারী। এই কী,Scene
ক্লাসের সাথে মিলিত হয়ে স্বতন্ত্রতা নিশ্চিত করে, প্রাথমিকভাবে অ্যানিমেশনের উদ্দেশ্যে। -
entries: List<NavEntry<T>>
: এটিNavEntry
অবজেক্টের একটি তালিকা যা প্রদর্শনের জন্যScene
দায়ী। গুরুত্বপূর্ণভাবে, যদি একইNavEntry
একটি ট্রানজিশনের সময় একাধিকScenes
প্রদর্শিত হয় (যেমন, একটি শেয়ার্ড এলিমেন্ট ট্রানজিশনে), এর বিষয়বস্তু শুধুমাত্র সাম্প্রতিক টার্গেটScene
দ্বারা রেন্ডার করা হবে যা এটি প্রদর্শন করছে। -
previousEntries: List<NavEntry<T>>
: এই বৈশিষ্ট্যটিNavEntry
গুলিকে সংজ্ঞায়িত করে যা বর্তমানScene
থেকে "ব্যাক" অ্যাকশন ঘটলে ফলাফল হবে। সঠিক ভবিষ্যদ্বাণীমূলক ব্যাক স্টেট গণনা করার জন্য এটি অপরিহার্য, যাNavDisplay
সঠিক পূর্ববর্তী অবস্থায় অনুমান করতে এবং পরিবর্তন করার অনুমতি দেয়, যা একটি ভিন্ন শ্রেণী এবং/অথবা কী সহ একটি দৃশ্য হতে পারে। -
content: @Composable () -> Unit
: এটি একটি সংমিশ্রণযোগ্য ফাংশন যেখানে আপনি সংজ্ঞায়িত করেন কিভাবেScene
তারentries
এবং সেইScene
সাথে নির্দিষ্ট যেকোন আশেপাশের UI উপাদানগুলিকে রেন্ডার করে।
দৃশ্য কৌশল বুঝতে
একটি SceneStrategy
হল এমন একটি প্রক্রিয়া যা নির্ধারণ করে যে কীভাবে পিছনের স্ট্যাক থেকে NavEntry
গুলির একটি প্রদত্ত তালিকা সাজানো হবে এবং একটি Scene
রূপান্তরিত হবে। মূলত, বর্তমান ব্যাক স্ট্যাক এন্ট্রির সাথে উপস্থাপিত হলে, একটি SceneStrategy
নিজেকে দুটি মূল প্রশ্ন জিজ্ঞাসা করে:
- আমি কি এই এন্ট্রি থেকে একটি
Scene
তৈরি করতে পারি? যদিSceneStrategy
নির্ধারণ করে যে এটি প্রদত্তNavEntry
গুলি পরিচালনা করতে পারে এবং একটি অর্থপূর্ণScene
তৈরি করতে পারে (যেমন, একটি ডায়ালগ বা একটি মাল্টি-পেন লেআউট), এটি এগিয়ে যায়। অন্যথায়, এটিnull
রিটার্ন করে, অন্যান্য কৌশলগুলিকে একটিScene
তৈরি করার সুযোগ দেয়। - যদি তাই হয়, আমি কিভাবে
Scene?
একবার একটিSceneStrategy
এন্ট্রিগুলি পরিচালনা করার জন্য প্রতিশ্রুতিবদ্ধ হলে, এটি একটিScene
নির্মাণের দায়িত্ব নেয় এবং সেইScene
মধ্যে কীভাবে নির্দিষ্টNavEntry
গুলি প্রদর্শিত হবে তা নির্ধারণ করে৷
একটি SceneStrategy
এর মূল হল এর calculateScene
পদ্ধতি:
@Composable public fun calculateScene( entries: List<NavEntry<T>>, onBack: (count: Int) -> Unit, ): Scene<T>?
এই পদ্ধতিটি ব্যাক স্ট্যাক থেকে বর্তমান List<NavEntry<T>>
এবং একটি onBack
কলব্যাক নেয়। এটি একটি Scene<T>
প্রদান করা উচিত যদি এটি প্রদত্ত এন্ট্রি থেকে সফলভাবে একটি গঠন করতে পারে, অথবা যদি না পারে তাহলে null
।
SceneStrategy
একটি সুবিধাজনক then
ইনফিক্স ফাংশন প্রদান করে, যা আপনাকে একাধিক কৌশল একসাথে চেইন করতে দেয়। এটি একটি নমনীয় সিদ্ধান্ত নেওয়ার পাইপলাইন তৈরি করে যেখানে প্রতিটি কৌশল একটি Scene
গণনা করার চেষ্টা করতে পারে এবং যদি এটি না পারে তবে এটি চেইনের পরবর্তীটিতে অর্পণ করে।
কিভাবে দৃশ্য এবং দৃশ্য কৌশল একসাথে কাজ করে
NavDisplay
হল সেন্ট্রাল কম্পোজেবল যা আপনার ব্যাক স্ট্যাক পর্যবেক্ষণ করে এবং উপযুক্ত Scene
নির্ধারণ ও রেন্ডার করতে একটি SceneStrategy
ব্যবহার করে।
NavDisplay's sceneStrategy
পরামিতি একটি SceneStrategy
আশা করে যা প্রদর্শনের Scene
গণনা করার জন্য দায়ী। প্রদত্ত কৌশল (বা কৌশলের চেইন) দ্বারা কোনো Scene
গণনা করা না হলে, NavDisplay
স্বয়ংক্রিয়ভাবে একটি SinglePaneSceneStrategy
ব্যবহার করে ডিফল্টরূপে ফিরে আসে।
এখানে মিথস্ক্রিয়াটির একটি ভাঙ্গন রয়েছে:
- যখন আপনি আপনার ব্যাক স্ট্যাক থেকে কী যোগ করেন বা সরিয়ে দেন (যেমন,
backStack.add()
বাbackStack.removeLastOrNull()
ব্যবহার করে),NavDisplay
এই পরিবর্তনগুলি পর্যবেক্ষণ করে। -
NavDisplay
NavEntrys
এর বর্তমান তালিকা (ব্যাক স্ট্যাক কী থেকে প্রাপ্ত) কনফিগার করাSceneStrategy's calculateScene
পদ্ধতিতে পাস করে। - যদি
SceneStrategy
সফলভাবে একটিScene
ফিরিয়ে দেয়,NavDisplay
তারপর সেইScene
content
রেন্ডার করে।NavDisplay
এছাড়াওScene
বৈশিষ্ট্যের উপর ভিত্তি করে অ্যানিমেশন এবং ভবিষ্যদ্বাণীমূলক ব্যাক পরিচালনা করে।
উদাহরণ: একক ফলক বিন্যাস (ডিফল্ট আচরণ)
আপনার কাছে সবচেয়ে সহজ কাস্টম লেআউটটি হল একটি একক-প্যান ডিসপ্লে, যেটি ডিফল্ট আচরণ যদি অন্য কোন SceneStrategy
অগ্রাধিকার না নেয়।
data class SinglePaneScene<T : Any>( override val key: T, val entry: NavEntry<T>, override val previousEntries: List<NavEntry<T>>, ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(entry) override val content: @Composable () -> Unit = { entry.content.invoke(entry.key) } } /** * A [SceneStrategy] that always creates a 1-entry [Scene] simply displaying the last entry in the * list. */ public class SinglePaneSceneStrategy<T : Any> : SceneStrategy<T> { @Composable override fun calculateScene(entries: List<NavEntry<T>>, onBack: (Int) -> Unit): Scene<T> = SinglePaneScene( key = entries.last().key, entry = entries.last(), previousEntries = entries.dropLast(1) ) }
উদাহরণ: বেসিক টু-পেন লেআউট (কাস্টম দৃশ্য এবং কৌশল)
এই উদাহরণটি দেখায় কিভাবে একটি সাধারণ দ্বি-ফলক লেআউট তৈরি করা যায় যা দুটি শর্তের ভিত্তিতে সক্রিয় করা হয়:
- উইন্ডোর প্রস্থ দুটি প্যানে সমর্থন করার জন্য যথেষ্ট প্রশস্ত (যেমন, কমপক্ষে
WIDTH_DP_MEDIUM_LOWER_BOUND
)। - পিছনের স্ট্যাকের উপরের দুটি এন্ট্রি সুনির্দিষ্টভাবে নির্দিষ্ট মেটাডেটা ব্যবহার করে একটি দ্বি-ফলক লেআউটে প্রদর্শিত হওয়ার জন্য তাদের সমর্থন ঘোষণা করে।
নিম্নলিখিত স্নিপেটটি TwoPaneScene.kt
এবং TwoPaneSceneStrategy.kt
এর সম্মিলিত সোর্স কোড:
// --- TwoPaneScene --- /** * A custom [Scene] that displays two [NavEntry]s side-by-side in a 50/50 split. */ class TwoPaneScene<T : Any>( override val key: Any, override val previousEntries: List<NavEntry<T>>, val firstEntry: NavEntry<T>, val secondEntry: NavEntry<T> ) : Scene<T> { override val entries: List<NavEntry<T>> = listOf(firstEntry, secondEntry) override val content: @Composable (() -> Unit) = { Row(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.weight(0.5f)) { firstEntry.content.invoke(firstEntry.key) } Column(modifier = Modifier.weight(0.5f)) { secondEntry.content.invoke(secondEntry.key) } } } companion object { internal const val TWO_PANE_KEY = "TwoPane" /** * Helper function to add metadata to a [NavEntry] indicating it can be displayed * in a two-pane layout. */ fun twoPane() = mapOf(TWO_PANE_KEY to true) } } // --- TwoPaneSceneStrategy --- /** * A [SceneStrategy] that activates a [TwoPaneScene] if the window is wide enough * and the top two back stack entries declare support for two-pane display. */ class TwoPaneSceneStrategy<T : Any> : SceneStrategy<T> { @OptIn(ExperimentalMaterial3AdaptiveApi::class, ExperimentalMaterial3WindowSizeClassApi::class) @Composable override fun calculateScene( entries: List<NavEntry<T>>, onBack: (Int) -> Unit ): Scene<T>? { val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass // Condition 1: Only return a Scene if the window is sufficiently wide to render two panes. // We use isWidthAtLeastBreakpoint with WIDTH_DP_MEDIUM_LOWER_BOUND (600dp). if (!windowSizeClass.isWidthAtLeastBreakpoint(WIDTH_DP_MEDIUM_LOWER_BOUND)) { return null } val lastTwoEntries = entries.takeLast(2) // Condition 2: Only return a Scene if there are two entries, and both have declared // they can be displayed in a two pane scene. return if (lastTwoEntries.size == 2 && lastTwoEntries.all { it.metadata.containsKey(TwoPaneScene.TWO_PANE_KEY) } ) { val firstEntry = lastTwoEntries.first() val secondEntry = lastTwoEntries.last() // The scene key must uniquely represent the state of the scene. val sceneKey = Pair(firstEntry.key, secondEntry.key) TwoPaneScene( key = sceneKey, // Where we go back to is a UX decision. In this case, we only remove the top // entry from the back stack, despite displaying two entries in this scene. // This is because in this app we only ever add one entry to the // back stack at a time. It would therefore be confusing to the user to add one // when navigating forward, but remove two when navigating back. previousEntries = entries.dropLast(1), firstEntry = firstEntry, secondEntry = secondEntry ) } else { null } } }
আপনার NavDisplay
এ এই TwoPaneSceneStrategy
ব্যবহার করার জন্য, আপনার entryProvider
কলগুলি পরিবর্তন করুন যাতে আপনি টু-প্যান লেআউটে দেখাতে চান এমন এন্ট্রিগুলির জন্য TwoPaneScene.twoPane()
মেটাডেটা অন্তর্ভুক্ত করুন। তারপর, একক-ফলক পরিস্থিতিগুলির জন্য ডিফল্ট ফলব্যাকের উপর নির্ভর করে, আপনার sceneStrategy
হিসাবে TwoPaneSceneStrategy()
প্রদান করুন:
// Define your navigation keys @Serializable data object ProductList : NavKey @Serializable data class ProductDetail(val id: String) : NavKey @Composable fun MyAppContent() { val backStack = rememberNavBackStack(ProductList) NavDisplay( backStack = backStack, entryProvider = entryProvider { entry<ProductList>( // Mark this entry as eligible for two-pane display metadata = TwoPaneScene.twoPane() ) { key -> Column { Text("Product List") Button(onClick = { backStack.add(ProductDetail("ABC")) }) { Text("View Details for ABC (Two-Pane Eligible)") } } } entry<ProductDetail>( // Mark this entry as eligible for two-pane display metadata = TwoPaneScene.twoPane() ) { key -> Text("Product Detail: ${key.id} (Two-Pane Eligible)") } // ... other entries ... }, // Simply provide your custom strategy. NavDisplay will fall back to SinglePaneSceneStrategy automatically. sceneStrategy = TwoPaneSceneStrategy<Any>(), onBack = { count -> repeat(count) { if (backStack.isNotEmpty()) { backStack.removeLastOrNull() } } } ) }
একটি উপাদান অভিযোজিত দৃশ্যে তালিকা-বিশদ সামগ্রী প্রদর্শন করুন
তালিকা-বিশদ ব্যবহারের ক্ষেত্রে , androidx.compose.material3.adaptive:adaptive-navigation3
আর্টিফ্যাক্ট একটি ListDetailSceneStrategy
প্রদান করে যা একটি তালিকা-বিশদ Scene
তৈরি করে। এই Scene
স্বয়ংক্রিয়ভাবে জটিল মাল্টি-পেন ব্যবস্থা (তালিকা, বিস্তারিত এবং অতিরিক্ত প্যান) পরিচালনা করে এবং উইন্ডোর আকার এবং ডিভাইসের অবস্থার উপর ভিত্তি করে এগুলিকে অভিযোজিত করে।
একটি উপাদান তালিকা-বিশদ Scene
তৈরি করতে, এই পদক্ষেপগুলি অনুসরণ করুন:
- নির্ভরতা যোগ করুন : আপনার প্রকল্পের
build.gradle.kts
ফাইলেandroidx.compose.material3.adaptive:adaptive-navigation3
অন্তর্ভুক্ত করুন। -
ListDetailSceneStrategy
মেটাডেটা দিয়ে আপনার এন্ট্রি সংজ্ঞায়িত করুন : উপযুক্ত ফলক প্রদর্শনের জন্য আপনারNavEntrys
চিহ্নিত করতেlistPane(), detailPane()
, এবংextraPane()
ব্যবহার করুন।listPane()
হেল্পার আপনাকে একটিdetailPlaceholder
নির্দিষ্ট করার অনুমতি দেয় যখন কোনো আইটেম নির্বাচন করা হয় না। -
rememberListDetailSceneStrategy
(): এই সংমিশ্রণযোগ্য ফাংশনটি একটি পূর্ব-কনফিগার করাListDetailSceneStrategy
প্রদান করে যা একটিNavDisplay
দ্বারা ব্যবহার করা যেতে পারে।
নিম্নলিখিত স্নিপেট হল একটি নমুনা Activity
যা ListDetailSceneStrategy
এর ব্যবহার প্রদর্শন করে:
@Serializable object ProductList : NavKey @Serializable data class ProductDetail(val id: String) : NavKey @Serializable data object Profile : NavKey class MaterialListDetailActivity : ComponentActivity() { @OptIn(ExperimentalMaterial3AdaptiveApi::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Scaffold { paddingValues -> val backStack = rememberNavBackStack(ProductList) val listDetailStrategy = rememberListDetailSceneStrategy<Any>() NavDisplay( backStack = backStack, modifier = Modifier.padding(paddingValues), onBack = { keysToRemove -> repeat(keysToRemove) { backStack.removeLastOrNull() } }, sceneStrategy = listDetailStrategy, entryProvider = entryProvider { entry<ProductList>( metadata = ListDetailSceneStrategy.listPane( detailPlaceholder = { ContentYellow("Choose a product from the list") } ) ) { ContentRed("Welcome to Nav3") { Button(onClick = { backStack.add(ProductDetail("ABC")) }) { Text("View product") } } } entry<ProductDetail>( metadata = ListDetailSceneStrategy.detailPane() ) { product -> ContentBlue("Product ${product.id} ", Modifier.background(PastelBlue)) { Column(horizontalAlignment = Alignment.CenterHorizontally) { Button(onClick = { backStack.add(Profile) }) { Text("View profile") } } } } entry<Profile>( metadata = ListDetailSceneStrategy.extraPane() ) { ContentGreen("Profile") } } ) } } } }