0Pricing
Kotlin Academy · درس

‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم

استخدم StateFlow كحاوية تفاعلية للحالة وراقبها في ViewModels.

‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

ما هو StateFlow؟

‏StateFlow هو تدفق ساخن يحتفظ بالحالة، وتكون له قيمة دائمًا، ويصدر التحديثات إلى جميع الجامعين. ويحل محل LiveData في بنى Kotlin الحديثة.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val stateFlow = MutableStateFlow(0)  // initial value
    println("Current: ${stateFlow.value}")
    stateFlow.value = 42
    println("Updated: ${stateFlow.value}")
}

MutableStateFlow في مقابل StateFlow

‏MutableStateFlow هو التطبيق القابل للتعديل المستخدم داخليًا. اعرض StateFlow للقراءة فقط على المراقبين الخارجيين، إما بالتحويل أو باستخدام .asStateFlow().

import kotlinx.coroutines.flow.*
class CounterViewModel {
    private val _count = MutableStateFlow(0)  // mutable internally
    val count: StateFlow<Int> = _count.asStateFlow()  // read-only externally
    fun increment() { _count.value++ }
    fun decrement() { _count.value-- }
}

جمع StateFlow

اجمع StateFlow كما تجمع أي Flow آخر. وتُصدر أحدث قيمة فور بدء الجمع.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val state = MutableStateFlow("loading")
    launch {
        state.collect { println("State: $it") }
    }
    delay(50); state.value = "success"
    delay(50); state.value = "idle"
    delay(50)
    coroutineContext.cancelChildren()
}

StateFlow يدمج القيم المتطابقة

لا يُصدر StateFlow قيمة إلا عند تغيرها. فإذا عيّنت القيمة نفسها مرتين، فسيحدث إصدار واحد فقط؛ إذ يدمج القيم المتطابقة المتتالية.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow = MutableStateFlow("A")
    launch {
        flow.collect { println("Received: $it") }
    }
    delay(50)
    flow.value = "A"  // no emission — same value
    flow.value = "B"  // emits
    flow.value = "B"  // no emission — same value
    flow.value = "C"  // emits
    delay(50)
    coroutineContext.cancelChildren()
}

StateFlow في ViewModel

النمط المعتمد في ViewModel هو: تعديل MutableStateFlow الخاص بواسطة منطق الأعمال، ومراقبة StateFlow العام من واجهة المستخدم.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
sealed class UiState { object Loading : UiState(); data class Success(val data: String) : UiState(); data class Error(val msg: String) : UiState() }
class MyViewModel(scope: CoroutineScope) {
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()
    init {
        scope.launch {
            delay(100)  // simulate load
            _uiState.value = UiState.Success("Hello!")
        }
    }
}

update() للتعديل الذري

استخدم update { } لتحديث الحالة ذريًا استنادًا إلى القيمة الحالية، وهو أمر مهم عند إجراء تحديثات متزامنة.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val counter = MutableStateFlow(0)
    List(100) {
        launch(Dispatchers.Default) {
            counter.update { it + 1 }  // thread-safe atomic update
        }
    }.forEach { it.join() }
    println("Counter: ${counter.value}")  // 100
}

compareAndSet للتحديثات التفاؤلية

لا يعيّن compareAndSet(expect, update) القيمة الجديدة إلا إذا تطابقت القيمة الحالية مع expect. ويفيد ذلك في التزامن التفاؤلي.

import kotlinx.coroutines.flow.*
fun main() {
    val state = MutableStateFlow("idle")
    val changed = state.compareAndSet("idle", "loading")
    println("Changed: $changed, Value: ${state.value}")  // true, loading
    val changed2 = state.compareAndSet("idle", "error")  // won't change
    println("Changed: $changed2, Value: ${state.value}") // false, loading
}

StateFlow في مقابل LiveData

يعمل StateFlow دون دورة حياة Android. أما LiveData فهو يراعي دورة الحياة، لكنه يقتصر على Android. يُفضّل استخدام StateFlow مع ViewModels المشتركة في KMP.

import kotlinx.coroutines.flow.*
// StateFlow: pure Kotlin, works in commonMain
// val state = MutableStateFlow("value")

// LiveData: Android-only, lifecycle-aware
// val liveData = MutableLiveData("value")

// In Compose, collect StateFlow with:
// val state by viewModel.uiState.collectAsState()
fun main() { println("StateFlow: multiplatform; LiveData: Android-only") }

stateIn: تحويل Cold Flow إلى StateFlow

يحوّل flow.stateIn(scope, started, initialValue) تدفقًا باردًا إلى StateFlow ساخن، ويشارك اشتراكًا واحدًا بين جميع الجامعين.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val cold = flow {
        println("Fetching...")
        delay(100)
        emit("data")
    }
    val hot: StateFlow<String> = cold.stateIn(
        scope = this,
        started = SharingStarted.Lazily,
        initialValue = "loading"
    )
    launch { hot.collect { println("A: $it") } }
    launch { hot.collect { println("B: $it") } }
    delay(200)
    coroutineContext.cancelChildren()
}

استراتيجيات SharingStarted

Eagerly: يبدأ فورًا. Lazily: يبدأ عند أول جامع. WhileSubscribed(stopTimeout): يتوقف عند عدم وجود جامعين، ويُعاد تشغيله عند اشتراك جامع.

import kotlinx.coroutines.flow.*
// Eagerly: upstream starts right away
// SharingStarted.Eagerly

// Lazily: waits for first subscriber
// SharingStarted.Lazily

// WhileSubscribed: stops 5s after last subscriber
// SharingStarted.WhileSubscribed(5000)

// Typical ViewModel usage:
// val uiState = repo.dataFlow.stateIn(viewModelScope, WhileSubscribed(5000), Loading)

مراقبة الحالة المشتقة

اشتق StateFlow جديدًا من StateFlow موجود باستخدام map + stateIn. ويُحدّث التدفق المشتق كلما تغير المصدر.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val count = MutableStateFlow(5)
    val doubled = count.map { it * 2 }.stateIn(this, SharingStarted.Eagerly, 10)
    println(doubled.value)  // 10
    count.value = 7
    delay(50)
    println(doubled.value)  // 14
    coroutineContext.cancelChildren()
}

تحقق سريع

ماذا يفعل StateFlow عند تعيين القيمة نفسها مرتين؟

مراجعة

‏StateFlow هو Flow ساخن له قيمة دائمًا ويدمج القيم المتطابقة. استخدم MutableStateFlow داخليًا، واعرض StateFlow خارجيًا. واستخدم update() للتعديلات المتزامنة وstateIn() لتحويل التدفقات الباردة.

الأسئلة الشائعة

هل درس «‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم» مجاني؟

نعم — نص درس «‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم»؟

استخدم StateFlow كحاوية تفاعلية للحالة وراقبها في ViewModels. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟

لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟

نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ‏StateFlow: حاوية حالة ساخنة لواجهة المستخدم
  2. ‏SharedFlow: حافلات الأحداث والأحداث أحادية الإرسال
  3. تحويل Flow البارد إلى ساخن باستخدام shareIn وstateIn
  4. اختبار StateFlow وSharedFlow باستخدام Turbine
← العودة إلى Kotlin Academy