UI রাজ্য উত্পাদন

আধুনিক UI খুব কমই স্থির থাকে। ব্যবহারকারী যখন UI-এর সাথে ইন্টারঅ্যাক্ট করে অথবা যখন অ্যাপটিকে নতুন ডেটা প্রদর্শন করতে হয়, তখন UI-এর অবস্থা পরিবর্তিত হয়।

এই নথিটি UI স্টেট তৈরি এবং পরিচালনার জন্য নির্দেশিকা প্রদান করে। এটি আপনাকে নিম্নলিখিত বিষয়গুলি বুঝতে সাহায্য করার উদ্দেশ্যে তৈরি:

  • UI স্টেট তৈরি করতে কোন API ব্যবহার করতে হবে। এটি একমুখী ডেটা প্রবাহের নীতি অনুসরণ করে আপনার স্টেট হোল্ডারগুলিতে উপলব্ধ স্টেট পরিবর্তনের উৎসগুলির প্রকৃতির উপর নির্ভর করে।
  • সিস্টেম রিসোর্সের কথা মাথায় রেখে UI স্টেট তৈরির পরিধি কীভাবে নির্ধারণ করা যায়।
  • UI-এর ব্যবহারের জন্য UI স্টেট কীভাবে প্রকাশ করা যায়।

মূলত, স্টেট প্রোডাকশন হলো UI স্টেটে এই পরিবর্তনগুলোর ক্রমবর্ধমান প্রয়োগ। স্টেট সর্বদা বিদ্যমান থাকে এবং ইভেন্টের ফলে এটি পরিবর্তিত হয়। ইভেন্ট এবং স্টেটের মধ্যে পার্থক্যগুলো নিচের টেবিলে সংক্ষেপে তুলে ধরা হলো:

ইভেন্টগুলি রাজ্য
ক্ষণস্থায়ী, অপ্রত্যাশিত এবং একটি সসীম সময়ের জন্য বিদ্যমান। সর্বদা বিদ্যমান।
রাষ্ট্রীয় উৎপাদনের উপকরণসমূহ। রাষ্ট্রীয় উৎপাদনের ফলাফল।
UI বা অন্যান্য উৎসের ফল। UI দ্বারা ব্যবহৃত হয়।

উপরের বিষয়টিকে সংক্ষেপে বোঝানোর জন্য একটি চমৎকার স্মারক হলো : অবস্থা হলো; ঘটনা ঘটে । নিচের ডায়াগ্রামটি একটি টাইমলাইনে ঘটনা ঘটার সাথে সাথে অবস্থার পরিবর্তনকে কল্পনা করতে সাহায্য করে। প্রতিটি ঘটনা উপযুক্ত অবস্থা ধারক দ্বারা প্রক্রিয়াজাত হয় এবং এর ফলে অবস্থার পরিবর্তন ঘটে:

ঘটনা বনাম অবস্থা
চিত্র ১ : ঘটনা অবস্থার পরিবর্তন ঘটায়

ঘটনাগুলো নিম্নলিখিত উৎস থেকে আসতে পারে:

  • ব্যবহারকারীগণ : যখন তাঁরা অ্যাপটির ইউজার ইন্টারফেসের সাথে মিথস্ক্রিয়া করেন।
  • অবস্থা পরিবর্তনের অন্যান্য উৎস : এপিআই (API) যেগুলো ইউআই (UI), ডোমেইন বা ডেটা লেয়ার থেকে অ্যাপ ডেটা উপস্থাপন করে, যেমন যথাক্রমে স্নাকবার টাইমআউট ইভেন্ট, ইউজ কেস বা রিপোজিটরি।

UI রাজ্য উৎপাদন পাইপলাইন

অ্যান্ড্রয়েড অ্যাপে স্টেট তৈরি হওয়ার প্রক্রিয়াকে নিম্নলিখিত বিষয়গুলো নিয়ে গঠিত একটি প্রসেসিং পাইপলাইন হিসেবে বিবেচনা করা যেতে পারে:

  • ইনপুট : অবস্থা পরিবর্তনের উৎসসমূহ। এগুলো হতে পারে:
    • UI লেয়ারের স্থানীয় বিষয়: এগুলি হতে পারে ইউজার ইভেন্ট, যেমন কোনো টাস্ক ম্যানেজমেন্ট অ্যাপে ব্যবহারকারীর "টু-ডু" কাজের জন্য শিরোনাম দেওয়া, অথবা এমন API যা UI লজিকে অ্যাক্সেস দেয় এবং UI স্টেটে পরিবর্তন আনে—উদাহরণস্বরূপ, Jetpack Compose-এ DrawerState এর open মেথড কল করা।
    • UI লেয়ারের বাইরের উৎস: এগুলো হলো ডোমেইন বা ডেটা লেয়ারের এমন সব উৎস যা UI অবস্থার পরিবর্তন ঘটায়—উদাহরণস্বরূপ, NewsRepository থেকে লোড হওয়া কোনো সংবাদ বা অন্যান্য ইভেন্ট।
    • উপরোক্ত বিষয়গুলোর মিশ্রণ।
  • স্টেট হোল্ডার : এমন টাইপ যারা স্টেট পরিবর্তনের উৎসগুলিতে বিজনেস লজিকUI লজিক প্রয়োগ করে এবং UI স্টেট তৈরি করার জন্য ইউজার ইভেন্ট প্রসেস করে।
  • আউটপুট : ইউআই স্টেট যা অ্যাপটি ব্যবহারকারীদের প্রয়োজনীয় তথ্য প্রদানের জন্য রেন্ডার করতে পারে।
রাজ্য উৎপাদন পাইপলাইন
চিত্র ২ : রাজ্য উৎপাদন পাইপলাইন

রাষ্ট্রীয় উৎপাদন এপিআই

আপনি পাইপলাইনের কোন পর্যায়ে আছেন তার উপর নির্ভর করে স্টেট তৈরিতে দুটি প্রধান এপিআই ব্যবহৃত হয়:

পাইপলাইন পর্যায় এপিআই
ইনপুট UI-কে জ্যাঙ্ক-মুক্ত রাখতে UI থ্রেডের বাইরে কাজ সম্পাদনের জন্য Coroutines এবং Flows-এর মতো অ্যাসিঙ্ক্রোনাস API ব্যবহার করুন।
আউটপুট স্টেট পরিবর্তিত হলে UI-কে বাতিল ও পুনরায় রেন্ডার করতে Compose State বা StateFlow-এর মতো অবজার্ভেবল ডেটা হোল্ডার API ব্যবহার করুন। অবজার্ভেবল ডেটা হোল্ডারগুলো নিশ্চিত করে যে স্ক্রিনে প্রদর্শনের জন্য UI-এর সর্বদা একটি স্টেট থাকে।

আউটপুটের জন্য অবজার্ভেবল এপিআই (Observable API) পছন্দের চেয়ে ইনপুটের জন্য অ্যাসিঙ্ক্রোনাস এপিআই (asynchronous API) পছন্দ স্টেট প্রোডাকশন পাইপলাইনের প্রকৃতির উপর বেশি প্রভাব ফেলে। এর কারণ হলো, ইনপুটগুলোই নির্ধারণ করে দেয় যে পাইপলাইনটিতে কী ধরনের প্রসেসিং প্রয়োগ করা যাবে

রাষ্ট্রীয় উৎপাদন পাইপলাইন সমাবেশ

পরবর্তী বিভাগগুলিতে বিভিন্ন ইনপুটের জন্য সবচেয়ে উপযুক্ত স্টেট প্রোডাকশন কৌশল এবং তার সাথে সামঞ্জস্যপূর্ণ আউটপুট এপিআইগুলি আলোচনা করা হয়েছে। প্রতিটি স্টেট প্রোডাকশন পাইপলাইন হলো ইনপুট এবং আউটপুটের একটি সমন্বয় এবং এটিকে অবশ্যই নিম্নলিখিত বৈশিষ্ট্যযুক্ত হতে হবে:

  • লাইফসাইকেল সচেতন : যখন UI দৃশ্যমান বা সক্রিয় থাকে না, তখন সুস্পষ্টভাবে প্রয়োজন না হলে স্টেট প্রোডাকশন পাইপলাইন কোনো রিসোর্স ব্যবহার করবে না।
  • সহজে ব্যবহারযোগ্য : UI-কে অবশ্যই উৎপাদিত UI স্টেট সহজে রেন্ডার করতে সক্ষম হতে হবে। Jetpack Compose-এ, UI-এর জন্য স্টেট ব্যবহার অত্যন্ত গুরুত্বপূর্ণ, কারণ স্টেটের পরিবর্তনের উপর ভিত্তি করে কম্পোজেবলগুলো আপডেট হতে পারে।

রাজ্য উৎপাদন পাইপলাইনে উপকরণ

একটি অবস্থা উৎপাদন পাইপলাইনের ইনপুটগুলো নিম্নলিখিত উপায়ে তাদের অবস্থা পরিবর্তনের উৎস সরবরাহ করে থাকে:

  • এককালীন অপারেশন যা সিনক্রোনাস বা অ্যাসিনক্রোনাস হতে পারে—উদাহরণস্বরূপ, suspend ফাংশন কল।
  • স্ট্রিম এপিআই—উদাহরণস্বরূপ, Flows
  • উপরের সবগুলোই।

নিম্নলিখিত বিভাগগুলিতে আলোচনা করা হয়েছে কীভাবে আপনি উপরোক্ত প্রতিটি ইনপুটের জন্য একটি স্টেট প্রোডাকশন পাইপলাইন তৈরি করতে পারেন।

অবস্থা পরিবর্তনের উৎস হিসেবে ওয়ান-শট এপিআই

অবজার্ভেবল ডেটা হোল্ডার ব্যবহার করে স্টেট ম্যানেজ করুন। mutableStateOf এপিআই ব্যবহার করুন, বিশেষ করে যখন কম্পোজ টেক্সট এপিআই (Compose text APIs) নিয়ে কাজ করছেন। আরও জটিল স্টেট ম্যানেজমেন্টের জন্য অথবা অন্যান্য আর্কিটেকচারাল কম্পোনেন্টের সাথে ইন্টিগ্রেট করার ক্ষেত্রে MutableStateFlow ) এপিআই ব্যবহার করুন। উভয় এপিআই-তেই এমন মেথড রয়েছে যা তাদের ধারণ করা ভ্যালুগুলোকে নিরাপদে অ্যাটমিক আপডেট করার সুযোগ দেয়, আপডেটগুলো সিনক্রোনাস বা অ্যাসিনক্রোনাস যাই হোক না কেন।

উদাহরণস্বরূপ, একটি সাধারণ পাশা খেলার অ্যাপের স্টেট আপডেটের কথা বিবেচনা করুন। ব্যবহারকারীর প্রতিটি পাশা নিক্ষেপ সিনক্রোনাস Random.nextInt মেথডটিকে কল করে এবং এর ফলাফল UI স্টেটে লেখা হয়।

রচনা অবস্থা

@Stable
interface DiceUiState {
    val firstDieValue: Int?
    val secondDieValue: Int?
    val numberOfRolls: Int?
}

private class MutableDiceUiState: DiceUiState {
    override var firstDieValue: Int? by mutableStateOf(null)
    override var secondDieValue: Int? by mutableStateOf(null)
    override var numberOfRolls: Int by mutableStateOf(0)
}

class DiceRollViewModel : ViewModel() {

    private val _uiState = MutableDiceUiState()
    val uiState: DiceUiState = _uiState

    // Called from the UI
    fun rollDice() {
        _uiState.firstDieValue = Random.nextInt(from = 1, until = 7)
        _uiState.secondDieValue = Random.nextInt(from = 1, until = 7)
        _uiState.numberOfRolls = _uiState.numberOfRolls + 1
    }
}

স্টেটফ্লো

data class DiceUiState(
    val firstDieValue: Int? = null,
    val secondDieValue: Int? = null,
    val numberOfRolls: Int = 0,
)

class DiceRollViewModel : ViewModel() {

    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

    // Called from the UI
    fun rollDice() {
        _uiState.update { currentState ->
            currentState.copy(
            firstDieValue = Random.nextInt(from = 1, until = 7),
            secondDieValue = Random.nextInt(from = 1, until = 7),
            numberOfRolls = currentState.numberOfRolls + 1,
            )
        }
    }
}

অ্যাসিঙ্ক্রোনাস কল থেকে UI অবস্থার পরিবর্তন

যেসব স্টেট পরিবর্তনের জন্য অ্যাসিঙ্ক্রোনাস ফলাফলের প্রয়োজন হয়, সেগুলোর জন্য উপযুক্ত CoroutineScope এ একটি Coroutine চালু করুন। এর ফলে CoroutineScope বাতিল হয়ে গেলে অ্যাপটি তার পূর্বের কাজটি বাতিল করে দিতে পারে। এরপর স্টেট হোল্ডার সাসপেন্ড মেথড কলের ফলাফলটি UI স্টেট প্রকাশ করতে ব্যবহৃত অবজার্ভেবল API-তে লিখে রাখে।

উদাহরণস্বরূপ, Architecture স্যাম্পলের AddEditTaskViewModel টি বিবেচনা করুন। যখন suspending saveTask মেথডটি অ্যাসিঙ্ক্রোনাসভাবে একটি টাস্ক সেভ করে, তখন MutableStateFlow-এর update মেথডটি সেই স্টেট পরিবর্তনটিকে UI স্টেটে ছড়িয়ে দেয়।

রচনা অবস্থা

@Stable
interface AddEditTaskUiState {
    val title: String
    val description: String
    val isTaskCompleted: Boolean
    val isLoading: Boolean
    val userMessage: String?
    val isTaskSaved: Boolean
}

private class MutableAddEditTaskUiState : AddEditTaskUiState() {
    override var title: String by mutableStateOf("")
    override var description: String by mutableStateOf("")
    override var isTaskCompleted: Boolean by mutableStateOf(false)
    override var isLoading: Boolean by mutableStateOf(false)
    override var userMessage: String? by mutableStateOf<String?>(null)
    override var isTaskSaved: Boolean by mutableStateOf(false)
}

class AddEditTaskViewModel(...) : ViewModel() {

   private val _uiState = MutableAddEditTaskUiState()
   val uiState: AddEditTaskUiState = _uiState

   private fun createNewTask() {
        viewModelScope.launch {
            val newTask = Task(uiState.value.title, uiState.value.description)
            try {
                tasksRepository.saveTask(newTask)
                // Write data into the UI state.
                _uiState.isTaskSaved = true
            }
            catch(cancellationException: CancellationException) {
                throw cancellationException
            }
            catch(exception: Exception) {
                _uiState.userMessage = getErrorMessage(exception))
            }
        }
    }
}

স্টেটফ্লো

data class AddEditTaskUiState(
    val title: String = "",
    val description: String = "",
    val isTaskCompleted: Boolean = false,
    val isLoading: Boolean = false,
    val userMessage: String? = null,
    val isTaskSaved: Boolean = false
)

class AddEditTaskViewModel(...) : ViewModel() {

   private val _uiState = MutableStateFlow(AddEditTaskUiState())
   val uiState: StateFlow<AddEditTaskUiState> = _uiState.asStateFlow()

   private fun createNewTask() {
        viewModelScope.launch {
            val newTask = Task(uiState.value.title, uiState.value.description)
            try {
                tasksRepository.saveTask(newTask)
                // Write data into the UI state.
                _uiState.update {
                    it.copy(isTaskSaved = true)
                }
            }
            catch(cancellationException: CancellationException) {
                throw cancellationException
            }
            catch(exception: Exception) {
                _uiState.update {
                    it.copy(userMessage = getErrorMessage(exception))
                }
            }
        }
    }
}

ব্যাকগ্রাউন্ড থ্রেড থেকে UI অবস্থা পরিবর্তন করা

UI স্টেট তৈরির জন্য কোরাউটিনগুলোকে মূল ডিসপ্যাচারে চালু করাই শ্রেয়—অর্থাৎ, নিচের কোড স্নিপেটগুলোতে withContext ব্লকের বাইরে। তবে, যদি আপনার একটি ভিন্ন ব্যাকগ্রাউন্ড কনটেক্সটে UI স্টেট আপডেট করার প্রয়োজন হয়, তাহলে আপনি নিম্নলিখিত পদ্ধতিটি অনুসরণ করতে পারেন:

  • Coroutines-কে একটি ভিন্ন কনকারেন্ট কনটেক্সটে চালানোর জন্য withContext মেথডটি ব্যবহার করুন।
  • MutableStateFlow ব্যবহার করার সময়, যথারীতি update মেথডটি ব্যবহার করুন।
  • Compose State ব্যবহার করার সময়, কনকারেন্ট কনটেক্সটে State-এর অ্যাটমিক আপডেট নিশ্চিত করতে Snapshot.withMutableSnapshot মেথডটি ব্যবহার করুন।

উদাহরণস্বরূপ, ধরে নিন যে নীচের DiceRollViewModel কোড স্নিপেটে, SlowRandom.nextInt হলো একটি গণনাগতভাবে নিবিড় suspend ফাংশন, যাকে একটি সিপিইউ-বাউন্ড কো-রুটিন থেকে কল করতে হবে।

রচনা অবস্থা

class DiceRollViewModel(
    private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {

    private val _uiState = MutableDiceUiState()
    val uiState: DiceUiState = _uiState

  // Called from the UI
  fun rollDice() {
        viewModelScope.launch() {
            // Other Coroutines that may be called from the current context
            
            withContext(defaultDispatcher) {
                Snapshot.withMutableSnapshot {
                    _uiState.firstDieValue = SlowRandom.nextInt(from = 1, until = 7)
                    _uiState.secondDieValue = SlowRandom.nextInt(from = 1, until = 7)
                    _uiState.numberOfRolls = _uiState.numberOfRolls + 1
                }
            }
        }
    }
}

স্টেটফ্লো

class DiceRollViewModel(
    private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {

    private val _uiState = MutableStateFlow(DiceUiState())
    val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()

  // Called from the UI
  fun rollDice() {
        viewModelScope.launch() {
            // Other Coroutines that may be called from the current context
            
            withContext(defaultDispatcher) {
                _uiState.update { currentState ->
                    currentState.copy(
                        firstDieValue = SlowRandom.nextInt(from = 1, until = 7),
                        secondDieValue = SlowRandom.nextInt(from = 1, until = 7),
                        numberOfRolls = currentState.numberOfRolls + 1,
                    )
                }
            }
        }
    }
}

অবস্থা পরিবর্তনের উৎস হিসেবে স্ট্রিম এপিআই

অবস্থা পরিবর্তনের যেসব উৎস সময়ের সাথে সাথে একাধিক ধারায় মান উৎপাদন করে, সেসব উৎসের সমস্ত ফলাফলকে একত্রিত করে একটি সুসংহত রূপ দেওয়াই হলো অবস্থা উৎপাদনের একটি সহজবোধ্য পদ্ধতি।

Kotlin Flows ব্যবহার করার সময়, আপনি ` combin` ফাংশনের মাধ্যমে এটি করতে পারেন। এর একটি উদাহরণ `Now in Android` স্যাম্পলের ` InterestsViewModel এ দেখা যায়:

class InterestsViewModel(
    authorsRepository: AuthorsRepository,
    topicsRepository: TopicsRepository
) : ViewModel() {

    val uiState = combine(
        authorsRepository.getAuthorsStream(),
        topicsRepository.getTopicsStream(),
    ) { availableAuthors, availableTopics ->
        InterestsUiState.Interests(
            authors = availableAuthors,
            topics = availableTopics
        )
    }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = InterestsUiState.Loading
    )
}

StateFlows তৈরি করতে stateIn অপারেটরের ব্যবহার স্টেট প্রোডাকশন পাইপলাইনের কার্যকলাপের উপর UI-কে আরও সূক্ষ্ম নিয়ন্ত্রণ প্রদান করে, কারণ এটির কেবল তখনই সক্রিয় থাকার প্রয়োজন হতে পারে যখন UI দৃশ্যমান থাকে।

  • লাইফসাইকেল-সচেতন পদ্ধতিতে ফ্লো সংগ্রহ করার সময় যদি পাইপলাইনটি শুধুমাত্র UI দৃশ্যমান থাকলেই সক্রিয় থাকার প্রয়োজন হয়, তাহলে SharingStarted.WhileSubscribed ব্যবহার করুন।
  • যদি ব্যবহারকারী UI-তে ফিরে আসতে পারেন এমন সময়েও পাইপলাইনটি সক্রিয় রাখার প্রয়োজন হয়—অর্থাৎ, UI-টি ব্যাকস্ট্যাকে বা স্ক্রিনের বাইরে অন্য কোনো ট্যাবে থাকে—তাহলে SharingStarted.Lazily ব্যবহার করুন।

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

অবস্থা পরিবর্তনের উৎস হিসেবে ওয়ান-শট এবং স্ট্রিম এপিআই

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

ফ্লো-এর ক্ষেত্রে, এর মানে সাধারণত স্টেট পরিবর্তন প্রচার করার জন্য এক বা একাধিক প্রাইভেট ব্যাকিং MutableStateFlow ইনস্ট্যান্স তৈরি করা। আপনি Compose স্টেট থেকে স্ন্যাপশট ফ্লো-ও তৈরি করতে পারেন।

architecture-samples রিপোজিটরি থেকে TaskDetailViewModel টি বিবেচনা করুন। এর UI স্টেট বর্তমান টাস্কের জন্য একটি স্ট্রিম ( _task ) এবং একটি ওয়ান-শট সোর্স ( _isTaskDeleted )-এর উপর নির্ভর করে, যা টাস্কটি ডিলিট হয়ে গেলে আপডেট হয়। যখন কোনো টাস্ক ভুল আইডির কারণে ডাটাবেসে খুঁজে পাওয়া যায় না, এবং যখন ব্যবহারকারী সেটি ডিলিট করে দেয়, এই দুইয়ের মধ্যে পার্থক্য করার জন্য এই ফ্ল্যাগটি প্রয়োজন।

রচনা অবস্থা

class TaskDetailViewModel @Inject constructor(
    private val tasksRepository: TasksRepository,
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    private var _isTaskDeleted by mutableStateOf(false)
    private val _task = tasksRepository.getTaskStream(taskId)

    val uiState: StateFlow<TaskDetailUiState> = combine(
        snapshotFlow { _isTaskDeleted },
        _task
    ) { isTaskDeleted, taskAsync ->
        TaskDetailUiState(
            task = taskAsync.data,
            isTaskDeleted = isTaskDeleted
        )
    }
        // Convert the result to the appropriate observable API for the UI
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = TaskDetailUiState()
        )

    fun deleteTask() = viewModelScope.launch {
        tasksRepository.deleteTask(taskId)
        _isTaskDeleted = true
    }
}

স্টেটফ্লো

class TaskDetailViewModel @Inject constructor(
    private val tasksRepository: TasksRepository,
    savedStateHandle: SavedStateHandle
) : ViewModel() {

    private val _isTaskDeleted = MutableStateFlow(false)
    private val _task = tasksRepository.getTaskStream(taskId)

    val uiState: StateFlow<TaskDetailUiState> = combine(
        _isTaskDeleted,
        _task
    ) { isTaskDeleted, taskAsync ->
        TaskDetailUiState(
            task = taskAsync.data,
            isTaskDeleted = isTaskDeleted
        )
    }
        // Convert the result to the appropriate observable API for the UI
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = TaskDetailUiState()
        )

    fun deleteTask() = viewModelScope.launch {
        tasksRepository.deleteTask(taskId)
        _isTaskDeleted.update { true }
    }
}

রাষ্ট্রীয় উৎপাদন পাইপলাইনে আউটপুটের প্রকারভেদ

UI স্টেটের জন্য আউটপুট API-এর নির্বাচন এবং এর উপস্থাপনার ধরণ মূলত নির্ভর করে আপনার অ্যাপ UI রেন্ডার করার জন্য কোন API ব্যবহার করে, যেমন Compose। নেটিভ UI তৈরির জন্য Jetpack Compose হলো প্রস্তাবিত আধুনিক টুলকিট। এক্ষেত্রে বিবেচ্য বিষয়গুলো হলো:

Jetpack Compose ব্যবহার করার সময় আপনার স্টেট প্রোডাকশন পাইপলাইনের জন্য কোন API-গুলো ব্যবহার করতে হবে, তা নিচের সারণিতে সংক্ষেপে তুলে ধরা হলো:

ইনপুট আউটপুট
ওয়ান-শট এপিআই StateFlow বা কম্পোজ State
স্ট্রিম এপিআই StateFlow
ওয়ান-শট এবং স্ট্রিম এপিআই StateFlow

রাষ্ট্রীয় উৎপাদন পাইপলাইন প্রারম্ভিকীকরণ

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

যখন সম্ভব, সিস্টেম রিসোর্স সাশ্রয় করতে স্টেট প্রোডাকশন পাইপলাইনটি লেজিলি ইনিশিয়ালাইজ করুন। বাস্তবে, এর মানে হলো আউটপুটের কোনো কনজিউমার না আসা পর্যন্ত অপেক্ষা করা। Flow এপিআই-তে stateIn মেথডের started আর্গুমেন্টের মাধ্যমে এটি করা যায়। যেসব ক্ষেত্রে এটি প্রযোজ্য নয়, সেখানে স্টেট প্রোডাকশন পাইপলাইনটি স্পষ্টভাবে শুরু করার জন্য একটি আইডম্পোটেন্ট initialize ফাংশন সংজ্ঞায়িত করুন, যেমনটি নিম্নলিখিত কোড স্নিপেটে দেখানো হয়েছে:

class MyViewModel : ViewModel() {

    private var initializeCalled = false

    // This function is idempotent provided it is only called from the UI thread.
    @MainThread
    fun initialize() {
        if(initializeCalled) return
        initializeCalled = true

        viewModelScope.launch {
            // seed the state production pipeline
        }
    }
}

নমুনা

নিম্নলিখিত গুগল নমুনাগুলি UI স্তরে স্টেট তৈরির প্রক্রিয়া প্রদর্শন করে। এই নির্দেশনাটি বাস্তবে দেখতে এগুলি ঘুরে দেখুন:

অতিরিক্ত সম্পদ

UI স্টেট সম্পর্কে আরও তথ্যের জন্য, নিম্নলিখিত অতিরিক্ত রিসোর্সগুলো দেখুন:

ডকুমেন্টেশন

বিষয়বস্তু দেখুন

{% হুবহু %} {% endverbatim %} {% হুবহু %} {% endverbatim %}