যেখানে রাজ্য উত্তোলন করতে হবে

কম্পোজ অ্যাপ্লিকেশানে, যেখানে আপনি UI স্থিতিটি উত্তোলন করেন তা নির্ভর করে UI লজিক বা ব্যবসায়িক যুক্তির জন্য এটি প্রয়োজন কিনা। এই ডকুমেন্টটি এই দুটি প্রধান পরিস্থিতি তুলে ধরে।

ভাল অভ্যাস

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

সর্বনিম্ন সাধারণ পূর্বপুরুষও রচনার বাইরে হতে পারে। উদাহরণস্বরূপ, একটি ViewModel এ উত্তোলন করার সময় কারণ ব্যবসায়িক যুক্তি জড়িত।

এই পৃষ্ঠাটি এই সর্বোত্তম অনুশীলনকে বিশদভাবে ব্যাখ্যা করে এবং মনে রাখার জন্য একটি সতর্কতা বর্ণনা করে।

ইউআই স্টেট এবং ইউআই লজিকের ধরন

নীচে এই নথিতে ব্যবহৃত UI অবস্থা এবং যুক্তির প্রকারের সংজ্ঞা রয়েছে।

UI অবস্থা

UI স্টেট হল সেই সম্পত্তি যা UI বর্ণনা করে। দুই ধরনের UI অবস্থা আছে:

  • স্ক্রীন UI অবস্থা হল যা আপনাকে স্ক্রিনে প্রদর্শন করতে হবে। উদাহরণস্বরূপ, একটি NewsUiState ক্লাসে UI রেন্ডার করার জন্য প্রয়োজনীয় সংবাদ নিবন্ধ এবং অন্যান্য তথ্য থাকতে পারে। এই অবস্থাটি সাধারণত অনুক্রমের অন্যান্য স্তরগুলির সাথে সংযুক্ত থাকে কারণ এতে অ্যাপ ডেটা থাকে৷
  • UI এলিমেন্ট স্টেট বলতে বোঝায় UI এলিমেন্টের অন্তর্নিহিত বৈশিষ্ট্য যা তাদের রেন্ডার করার পদ্ধতিকে প্রভাবিত করে। একটি UI উপাদান দেখানো বা লুকানো হতে পারে এবং একটি নির্দিষ্ট ফন্ট, ফন্টের আকার বা ফন্টের রঙ থাকতে পারে। অ্যান্ড্রয়েড ভিউ-এ, ভিউ এই স্টেটটিকে নিজেই পরিচালনা করে কারণ এটি সহজাতভাবে স্টেটফুল, এটির অবস্থা পরিবর্তন বা অনুসন্ধান করার পদ্ধতিগুলি প্রকাশ করে৷ এর একটি উদাহরণ হল TextView ক্লাসের পাঠ্যের জন্য get এবং set পদ্ধতি। জেটপ্যাক কম্পোজে, স্টেট কম্পোজেবলের বাহ্যিক, এবং আপনি এটিকে কম্পোজেবলের আশেপাশে থেকে কলিং কম্পোজেবল ফাংশন বা স্টেট হোল্ডারে উত্তোলন করতে পারেন। এর একটি উদাহরণ হল Scaffold কম্পোজেবলের জন্য ScaffoldState

যুক্তিবিদ্যা

একটি অ্যাপ্লিকেশনে যুক্তি ব্যবসায়িক যুক্তি বা UI যুক্তি হতে পারে:

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

UI যুক্তি

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

নিচে সমাধানের বর্ণনা এবং কখন কোনটি ব্যবহার করতে হবে তার ব্যাখ্যা।

রাষ্ট্র মালিক হিসাবে composables

কম্পোজেবলে UI লজিক এবং UI এলিমেন্ট স্টেট থাকা একটি ভাল পন্থা যদি স্টেট এবং লজিক সহজ হয়। আপনি আপনার রাজ্যের অভ্যন্তরীণ একটি সংমিশ্রণযোগ্য বা প্রয়োজন অনুযায়ী উত্তোলন করতে পারেন।

কোন রাষ্ট্র উত্তোলন প্রয়োজন

উত্তোলন রাষ্ট্র সবসময় প্রয়োজন হয় না. স্টেটকে কম্পোজেবলের মধ্যে অভ্যন্তরীণ রাখা যেতে পারে যখন অন্য কোন কম্পোজেবলকে নিয়ন্ত্রণ করার প্রয়োজন হয় না। এই স্নিপেটে, একটি কম্পোজেবল রয়েছে যা ট্যাপে প্রসারিত এবং ভেঙে পড়ে:

@Composable
fun ChatBubble(
    message: Message
) {
    var showDetails by rememberSaveable { mutableStateOf(false) } // Define the UI element expanded state

    ClickableText(
        text = AnnotatedString(message.content),
        onClick = { showDetails = !showDetails } // Apply simple UI logic
    )

    if (showDetails) {
        Text(message.timestamp)
    }
}

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

composables মধ্যে উত্তোলন

আপনার যদি অন্য কম্পোজেবলের সাথে আপনার UI উপাদানের অবস্থা ভাগ করে নেওয়ার প্রয়োজন হয় এবং বিভিন্ন জায়গায় এটিতে UI লজিক প্রয়োগ করতে হয়, তাহলে আপনি এটিকে UI শ্রেণিবিন্যাসে উচ্চতর করতে পারেন। এটি আপনার কম্পোজেবলগুলিকে আরও পুনরায় ব্যবহারযোগ্য এবং পরীক্ষা করা সহজ করে তোলে।

নিম্নলিখিত উদাহরণ হল একটি চ্যাট অ্যাপ যা কার্যকারিতার দুটি অংশ প্রয়োগ করে:

  • JumpToBottom বোতামটি বার্তা তালিকাকে নীচে স্ক্রোল করে। বোতামটি তালিকার অবস্থায় UI লজিক সম্পাদন করে।
  • ব্যবহারকারী নতুন বার্তা পাঠানোর পরে MessagesList তালিকাটি নীচে স্ক্রোল করে। UserInput তালিকার অবস্থায় UI লজিক সম্পাদন করে।
একটি JumpToBottom বোতাম সহ চ্যাট অ্যাপ এবং নতুন বার্তাগুলিতে নীচে স্ক্রোল করুন৷
চিত্র 1. একটি JumpToBottom বোতাম সহ চ্যাট অ্যাপ এবং নতুন বার্তাগুলিতে নীচে স্ক্রোল করুন

কম্পোজযোগ্য শ্রেণিবিন্যাস নিম্নরূপ:

চ্যাট কম্পোজেবল গাছ
চিত্র 2. চ্যাট কম্পোজেবল ট্রি

LazyColumn স্টেটটি কথোপকথনের স্ক্রিনে উত্তোলন করা হয়েছে যাতে অ্যাপটি UI লজিক সঞ্চালন করতে পারে এবং প্রয়োজনীয় সমস্ত কম্পোজেবল থেকে স্টেট পড়তে পারে:

LazyColumn থেকে কথোপকথন স্ক্রীনে LazyColumn অবস্থা উত্তোলন করা
চিত্র 3. LazyColumn অবস্থা থেকে LazyColumn থেকে ConversationScreen উত্তোলন

সুতরাং অবশেষে composables হল:

LazyListState-এর সাথে চ্যাট কম্পোজেবল ট্রি কথোপকথনস্ক্রীনে উত্তোলন করা হয়েছে
চিত্র 4. LazyListState এর সাথে চ্যাট কম্পোজেবল ট্রি ConversationScreen উত্তোলন করা হয়েছে

কোডটি নিম্নরূপ:

@Composable
private fun ConversationScreen(/*...*/) {
    val scope = rememberCoroutineScope()

    val lazyListState = rememberLazyListState() // State hoisted to the ConversationScreen

    MessagesList(messages, lazyListState) // Reuse same state in MessageList

    UserInput(
        onMessageSent = { // Apply UI logic to lazyListState
            scope.launch {
                lazyListState.scrollToItem(0)
            }
        },
    )
}

@Composable
private fun MessagesList(
    messages: List<Message>,
    lazyListState: LazyListState = rememberLazyListState() // LazyListState has a default value
) {

    LazyColumn(
        state = lazyListState // Pass hoisted state to LazyColumn
    ) {
        items(messages, key = { message -> message.id }) { item ->
            Message(/*...*/)
        }
    }

    val scope = rememberCoroutineScope()

    JumpToBottom(onClicked = {
        scope.launch {
            lazyListState.scrollToItem(0) // UI logic being applied to lazyListState
        }
    })
}

LazyListState প্রয়োগ করতে হবে এমন UI যুক্তির জন্য যতটা প্রয়োজন ততটা উঁচু করা হয়। যেহেতু এটি একটি সংমিশ্রণযোগ্য ফাংশনে শুরু করা হয়, তাই এটির জীবনচক্র অনুসরণ করে কম্পোজিশনে সংরক্ষণ করা হয়।

মনে রাখবেন যে lazyListState MessagesList পদ্ধতিতে সংজ্ঞায়িত করা হয়েছে, rememberLazyListState() এর ডিফল্ট মান সহ। এটি রচনার একটি সাধারণ প্যাটার্ন। এটি কম্পোজেবলকে আরও পুনঃব্যবহারযোগ্য এবং নমনীয় করে তোলে। তারপরে আপনি অ্যাপের বিভিন্ন অংশে কম্পোজেবল ব্যবহার করতে পারেন যা রাজ্যকে নিয়ন্ত্রণ করার প্রয়োজন নাও হতে পারে। এটি সাধারণত একটি কম্পোজেবল পরীক্ষা বা পূর্বরূপ দেখার সময় হয়। ঠিক এইভাবে LazyColumn তার অবস্থাকে সংজ্ঞায়িত করে।

LazyListState-এর সর্বনিম্ন সাধারণ পূর্বপুরুষ হল ConversationScreen
চিত্র 5. LazyListState এর সর্বনিম্ন সাধারণ পূর্বপুরুষ হল ConversationScreen

রাষ্ট্রের মালিক হিসাবে প্লেইন স্টেট হোল্ডার ক্লাস

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

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

এই প্লেইন ক্লাস তৈরি করা হয় এবং কম্পোজিশনে মনে রাখা হয়। যেহেতু তারা কম্পোজেবলের জীবনচক্র অনুসরণ করে, তাই তারা কম্পোজ লাইব্রেরি দ্বারা প্রদত্ত প্রকারগুলি গ্রহণ করতে পারে যেমন rememberNavController() বা rememberLazyListState()

এর একটি উদাহরণ হল LazyListState প্লেইন স্টেট হোল্ডার ক্লাস, LazyColumn বা LazyRow এর UI জটিলতা নিয়ন্ত্রণ করতে কম্পোজে প্রয়োগ করা হয়েছে।

// LazyListState.kt

@Stable
class LazyListState constructor(
    firstVisibleItemIndex: Int = 0,
    firstVisibleItemScrollOffset: Int = 0
) : ScrollableState {
    /**
     *   The holder class for the current scroll position.
     */
    private val scrollPosition = LazyListScrollPosition(
        firstVisibleItemIndex, firstVisibleItemScrollOffset
    )

    suspend fun scrollToItem(/*...*/) { /*...*/ }

    override suspend fun scroll() { /*...*/ }

    suspend fun animateScrollToItem() { /*...*/ }
}

LazyListState এই UI উপাদানের জন্য scrollPosition সংরক্ষণ করে LazyColumn এর অবস্থাকে এনক্যাপসুলেট করে। এটি একটি প্রদত্ত আইটেম স্ক্রল করার জন্য স্ক্রোল অবস্থান পরিবর্তন করার পদ্ধতিগুলিও প্রকাশ করে৷

আপনি দেখতে পাচ্ছেন, একটি কম্পোজেবলের দায়িত্ব বৃদ্ধি করা একজন রাষ্ট্র ধারকের প্রয়োজনীয়তা বাড়ায় । দায়িত্বগুলি UI যুক্তিতে হতে পারে, বা ট্র্যাক রাখার জন্য রাজ্যের পরিমাণে।

আরেকটি সাধারণ প্যাটার্ন হল অ্যাপে রুট কম্পোজেবল ফাংশনগুলির জটিলতা পরিচালনা করতে একটি প্লেইন স্টেট হোল্ডার ক্লাস ব্যবহার করা। ন্যাভিগেশন স্টেট এবং স্ক্রিন সাইজিংয়ের মতো অ্যাপ-লেভেল স্টেট এনক্যাপসুলেট করতে আপনি এই ধরনের ক্লাস ব্যবহার করতে পারেন। এর একটি সম্পূর্ণ বিবরণ UI লজিক এবং এর স্টেট হোল্ডার পেজে পাওয়া যাবে।

ব্যবসায়িক যুক্তি

কম্পোজেবল এবং প্লেইন স্টেট হোল্ডার ক্লাস UI লজিক এবং UI এলিমেন্ট স্টেটের দায়িত্বে থাকলে, একটি স্ক্রীন লেভেল স্টেট হোল্ডার নিম্নলিখিত কাজের দায়িত্বে থাকে:

  • অ্যাপ্লিকেশানের ব্যবসায়িক যুক্তিতে অ্যাক্সেস প্রদান করা যা সাধারণত ব্যবসা এবং ডেটা স্তরগুলির মতো শ্রেণিবিন্যাসের অন্যান্য স্তরগুলিতে স্থাপন করা হয়।
  • একটি নির্দিষ্ট স্ক্রিনে উপস্থাপনার জন্য অ্যাপ্লিকেশন ডেটা প্রস্তুত করা হচ্ছে, যা স্ক্রীন UI অবস্থায় পরিণত হয়।

রাষ্ট্র মালিক হিসাবে মডেল দেখুন

অ্যান্ড্রয়েড ডেভেলপমেন্টে AAC ViewModels-এর সুবিধাগুলি তাদের ব্যবসায়িক যুক্তিতে অ্যাক্সেস প্রদান এবং স্ক্রিনে উপস্থাপনের জন্য অ্যাপ্লিকেশন ডেটা প্রস্তুত করার জন্য উপযুক্ত করে তোলে।

আপনি যখন ViewModel এ UI স্থিতি উত্তোলন করেন, তখন আপনি এটিকে রচনার বাইরে নিয়ে যান।

ViewModel-এ উত্তোলিত স্টেট কম্পোজিশনের বাইরে সংরক্ষণ করা হয়।
চিত্র 6. ViewModel এ উত্তোলিত স্টেট কম্পোজিশনের বাইরে সংরক্ষণ করা হয়।

ViewModels রচনা অংশ হিসাবে সংরক্ষণ করা হয় না. এগুলি ফ্রেমওয়ার্ক দ্বারা সরবরাহ করা হয়েছে এবং সেগুলি একটি ViewModelStoreOwner এর কাছে স্কোপ করা হয়েছে যা একটি কার্যকলাপ, খণ্ড, নেভিগেশন গ্রাফ বা নেভিগেশন গ্রাফের গন্তব্য হতে পারে৷ ViewModel স্কোপ সম্পর্কে আরও তথ্যের জন্য আপনি ডকুমেন্টেশন পর্যালোচনা করতে পারেন।

তারপর, ViewModel হল সত্যের উৎস এবং UI রাজ্যের সর্বনিম্ন সাধারণ পূর্বপুরুষ

স্ক্রীন UI অবস্থা

উপরের সংজ্ঞা অনুযায়ী, স্ক্রীন UI অবস্থা ব্যবসার নিয়ম প্রয়োগ করে তৈরি করা হয়। প্রদত্ত যে স্ক্রীন স্তরের স্টেট হোল্ডার এর জন্য দায়ী, এর অর্থ হল স্ক্রীন UI স্টেটটি সাধারণত স্ক্রীন স্তরের স্টেট হোল্ডারে উত্তোলন করা হয়, এই ক্ষেত্রে একটি ViewModel

একটি চ্যাট অ্যাপের ConversationViewModel বিবেচনা করুন এবং এটি কীভাবে স্ক্রীন UI অবস্থা এবং ইভেন্টগুলিকে সংশোধন করতে প্রকাশ করে:

class ConversationViewModel(
    channelId: String,
    messagesRepository: MessagesRepository
) : ViewModel() {

    val messages = messagesRepository
        .getLatestMessages(channelId)
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = emptyList()
        )

    // Business logic
    fun sendMessage(message: Message) { /* ... */ }
}

কম্পোজেবলগুলি ViewModel এ উত্তোলিত স্ক্রীন UI অবস্থা ব্যবহার করে। ব্যবসায়িক যুক্তিতে অ্যাক্সেস প্রদান করতে আপনার স্ক্রীন-স্তরের কম্পোজেবলগুলিতে ViewModel ইনস্ট্যান্সটি ইনজেক্ট করা উচিত।

নীচে একটি স্ক্রীন-স্তরের কম্পোজেবলে ব্যবহৃত একটি ViewModel উদাহরণ। এখানে, কম্পোজেবল ConversationScreen() ViewModel উত্তোলিত স্ক্রীন UI অবস্থাকে গ্রাস করে:

@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {

    val messages by conversationViewModel.messages.collectAsStateWithLifecycle()

    ConversationScreen(
        messages = messages,
        onSendMessage = { message: Message -> conversationViewModel.sendMessage(message) }
    )
}

@Composable
private fun ConversationScreen(
    messages: List<Message>,
    onSendMessage: (Message) -> Unit
) {

    MessagesList(messages, onSendMessage)
    /* ... */
}

সম্পত্তি তুরপুন

"প্রপার্টি ড্রিলিং" বলতে বোঝায় অনেক নেস্টেড চিলড্রেন কম্পোনেন্টের মাধ্যমে ডাটা যে স্থানে তারা পড়া হয় সেখানে পাঠানো।

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

যদিও পৃথক ল্যাম্বডা পরামিতি হিসাবে ইভেন্টগুলিকে প্রকাশ করা ফাংশন স্বাক্ষরকে ওভারলোড করতে পারে, এটি কম্পোজযোগ্য ফাংশনের দায়িত্বগুলি কী তা দৃশ্যমানতাকে সর্বাধিক করে তোলে৷ আপনি এক নজরে এটা কি দেখতে পারেন.

রাজ্য এবং ইভেন্টগুলিকে এক জায়গায় এনক্যাপসুলেট করার জন্য র‍্যাপার ক্লাস তৈরির চেয়ে সম্পত্তি ড্রিলিং পছন্দনীয় কারণ এটি সংমিশ্রণযোগ্য দায়িত্বগুলির দৃশ্যমানতা হ্রাস করে৷ র‍্যাপার ক্লাস না থাকার মাধ্যমে আপনি কম্পোজেবলগুলিকে শুধুমাত্র তাদের প্রয়োজনীয় প্যারামিটারগুলি পাস করার সম্ভাবনা বেশি, যা একটি সর্বোত্তম অনুশীলন

এই ইভেন্টগুলি নেভিগেশন ইভেন্ট হলে একই সর্বোত্তম অনুশীলন প্রযোজ্য, আপনি নেভিগেশন ডক্সে এটি সম্পর্কে আরও জানতে পারেন।

আপনি যদি একটি কর্মক্ষমতা সমস্যা চিহ্নিত করে থাকেন, তাহলে আপনি রাজ্যের পড়া স্থগিত করতেও বেছে নিতে পারেন। আপনি আরও জানতে পারফরম্যান্স ডক্স চেক করতে পারেন।

UI উপাদানের অবস্থা

আপনি স্ক্রীন লেভেল স্টেট হোল্ডারে UI এলিমেন্ট স্টেট হোস্ট করতে পারেন যদি এমন ব্যবসায়িক যুক্তি থাকে যা পড়তে বা লিখতে হয়।

একটি চ্যাট অ্যাপের উদাহরণ অব্যাহত রেখে, অ্যাপটি একটি গ্রুপ চ্যাটে ব্যবহারকারীর পরামর্শ প্রদর্শন করে যখন ব্যবহারকারী @ এবং একটি ইঙ্গিত টাইপ করে। এই পরামর্শগুলি ডেটা স্তর থেকে আসে এবং ব্যবহারকারীর পরামর্শগুলির একটি তালিকা গণনা করার যুক্তিকে ব্যবসায়িক যুক্তি হিসাবে বিবেচনা করা হয়। বৈশিষ্ট্য এই মত দেখায়:

বৈশিষ্ট্য যা একটি গ্রুপ চ্যাটে ব্যবহারকারীর পরামর্শ প্রদর্শন করে যখন ব্যবহারকারী `@` এবং একটি ইঙ্গিত টাইপ করে
চিত্র 7. বৈশিষ্ট্য যা একটি গ্রুপ চ্যাটে ব্যবহারকারীর পরামর্শ প্রদর্শন করে যখন ব্যবহারকারী @ টাইপ করে এবং একটি ইঙ্গিত দেয়

এই বৈশিষ্ট্যটি বাস্তবায়নকারী ViewModel টি নিম্নরূপ দেখাবে:

class ConversationViewModel(/*...*/) : ViewModel() {

    // Hoisted state
    var inputMessage by mutableStateOf("")
        private set

    val suggestions: StateFlow<List<Suggestion>> =
        snapshotFlow { inputMessage }
            .filter { hasSocialHandleHint(it) }
            .mapLatest { getHandle(it) }
            .mapLatest { repository.getSuggestions(it) }
            .stateIn(
                scope = viewModelScope,
                started = SharingStarted.WhileSubscribed(5_000),
                initialValue = emptyList()
            )

    fun updateInput(newInput: String) {
        inputMessage = newInput
    }
}

inputMessage হল একটি পরিবর্তনশীল যা TextField স্টেট সংরক্ষণ করে। ব্যবহারকারী যখনই নতুন ইনপুট টাইপ করে, অ্যাপটি suggestions দেওয়ার জন্য ব্যবসায়িক যুক্তিকে কল করে।

suggestions হল স্ক্রীন UI অবস্থা এবং StateFlow থেকে সংগ্রহ করে কম্পোজ UI থেকে ব্যবহার করা হয়।

সতর্কতা

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

ধরা যাক অ্যাপ ড্রয়ারের বিষয়বস্তুটি গতিশীল এবং এটি বন্ধ হওয়ার পরে আপনাকে ডেটা স্তর থেকে এটি আনতে এবং রিফ্রেশ করতে হবে। আপনার ড্রয়ারের অবস্থাটি ViewModel এ উত্তোলন করা উচিত যাতে আপনি রাষ্ট্রের মালিকের কাছ থেকে এই উপাদানটিতে UI এবং ব্যবসায়িক যুক্তি উভয়ই কল করতে পারেন।

যাইহোক, কম্পোজ UI থেকে viewModelScope ব্যবহার করে DrawerState close() পদ্ধতিতে কল করার ফলে IllegalStateException টাইপের একটি রানটাইম ব্যতিক্রম ঘটে যাতে একটি বার্তা লেখা থাকে যে "এই CoroutineContext” এ একটি MonotonicFrameClock উপলব্ধ নয়"।

এটি ঠিক করতে, কম্পোজিশনে স্কোপযুক্ত একটি CoroutineScope ব্যবহার করুন। এটি CoroutineContext এ একটি MonotonicFrameClock প্রদান করে যা সাসপেন্ড ফাংশন কাজ করার জন্য প্রয়োজনীয়।

এই ক্র্যাশটি ঠিক করতে, ViewModel এ coroutine-এর CoroutineContext কম্পোজিশনে স্কোপ করা একটিতে স্যুইচ করুন। এটি এই মত দেখতে পারে:

class ConversationViewModel(/*...*/) : ViewModel() {

    val drawerState = DrawerState(initialValue = DrawerValue.Closed)

    private val _drawerContent = MutableStateFlow(DrawerContent.Empty)
    val drawerContent: StateFlow<DrawerContent> = _drawerContent.asStateFlow()

    fun closeDrawer(uiScope: CoroutineScope) {
        viewModelScope.launch {
            withContext(uiScope.coroutineContext) { // Use instead of the default context
                drawerState.close()
            }
            // Fetch drawer content and update state
            _drawerContent.update { content }
        }
    }
}

// in Compose
@Composable
private fun ConversationScreen(
    conversationViewModel: ConversationViewModel = viewModel()
) {
    val scope = rememberCoroutineScope()

    ConversationScreen(onCloseDrawer = { conversationViewModel.closeDrawer(uiScope = scope) })
}

আরও জানুন

রাজ্য এবং জেটপ্যাক রচনা সম্পর্কে আরও জানতে, নিম্নলিখিত অতিরিক্ত সংস্থানগুলি দেখুন৷

নমুনা

কোডল্যাব

ভিডিও

{% শব্দার্থে %} {% endverbatim %} {% শব্দার্থে %} {% endverbatim %}