شرح بسيط لمفهوم viewModel و hilt في Kotlin compose

 

شرح بسيط لمفهوم viewModel و hilt في Kotlin compose
حقن التبعيات في Android: لماذا Hilt هو الحل الأمثل؟

كيف تجعل كود Android أكثر نظافة مع Hilt و ViewModel؟

في تطوير تطبيقات Android باستخدام Jetpack Compose، يعد استخدامك للـ ViewModel أداة مهمة لفصل واجهة المستخدم (UI) عن Logic الخاص بمشروعك. حيث في هذه المقالة، سنشرح لكم بإيجاز كيفية استخدام ViewModel وHilt معًا داخل مشروعك ، مع التركيز على كيفية قيام Hilt تلقائيًا بتوفير التعبئة التلقائيه إلى ViewModel، مما يجعل من السهل إدارة الحالة في التطبيقات الحديثة.


ما هو ViewModel؟

يعد ViewModel أحد مكونات مكتبة Android Jetpack المصممة لإدارة بيانات واجهة المستخدم والتي تساعد في فصل واجهة المستخدم (UI) عن اللوجيك الخاص بالمشروع الخاص بك. فهو يساعد في الحفاظ على البيانات أثناء تغييرات التكوين (مثل تدوير الجهاز) او الانتقال الى صفحات اخرى وهو مصمم ليكون نشطًا طالما أن واجهة المستخدم نفسها نشطة. وبشكل عام فإن ViewModel يمنع فقدان البيانات (Memory Leaks) ويحافظ على تناسق الحالة دون الحاجه الى إعادة تحميل البيانات في كل مرة.


ما هو Hilt ؟

Hilt هي مكتبة تعتمد على Dagger تستخدم لإدارة التبعيات. باستخدام مكتبه Hilt، والتي تمكنك من إعداد التبعيات وتكوينها تلقائيًا دون الحاجة إلى كتابة الكثير من الاكواد البرمجية. يُسهل Hilt توصيل ViewModel الخاص بك بكل التبعيات التي تحتاجها.


ما هو Dagger؟

Dagger هي مكتبة مفتوحة المصدر تُستخدم لتسهيل عملية حقن التبعيات في تطبيقات Android. حيث تركز Dagger على مبدأ إنشاء الكائنات وإدارتها تلقائيًا، مما يقلل من الحاجة إلى كتابة كود في كل مره


مميزات Dagger:

تقليل اعادة كتابه الاكواد : يقوم Dagger بإنشاء الكائنات وإدارتها تلقائيًا.

تحسين الأداء: يعتمد Dagger على معالجة الكود في وقت التجميع (Compile-time)، مما يجعله أسرع من بعض مكتبات حقن التبعيات الأخرى.

سهولة الاختبار: يسهل فصل التبعيات، مما يجعل الاختبار أسهل.

التكامل مع Android: يدعم دورة حياة Android مثل Activity وFragment.


كيف يعمل Dagger؟

Dagger يعتمد على ثلاثة مفاهيم رئيسية:

Module: هو الفصل الذي يحدد كيفية إنشاء الكائنات. يمكنك اعتباره "مصنعًا" للكائنات.

Component: هو الجسر الذي يربط بين Module والفصول التي تحتاج إلى التبعيات. يقوم Component بحقن التبعيات في الأماكن المطلوبة.

Inject: هو الواجهة التي تُستخدم لطلب التبعيات من Dagger.


كود لعمل counter باستخدام viewmodel بواسطة compose

@HiltViewModel
class CounterViewModel : ViewModel() {
    private val _state = MutableStateFlow<Int>(0)
    val state = _state.asStateFlow()

    fun add() {
        _state.update { it + 1 }
    }

    fun minus() {
        _state.update {
            if (it != 0) {
                it - 1
            } else {
                it
            }
        }
    }
}

تحتوي التعليمة البرمجية التالية على تطبيق مبدأ ViewModel الذي يدير حالة العداد البسيط. يتم استخدام Hilt لتوفير ViewModel وإدارته في Composable.


@HiltViewModel: هي annotation تُستخدم لتعريف أن هذا الـ ViewModel سيتم حقنه بواسطة Hilt.
CounterViewModel: هو الـ ViewModel الذي يحتوي على حالة عداد (_state) يتم تتبعها باستخدام MutableStateFlow.
add(): دالة لإضافة 1 إلى قيمة العداد.
minus(): دالة لإنقاص 1 من قيمة العداد إذا كانت القيمة أكبر من صفر.


MutableStateFlow:
MutableStateFlow هو نوع StateFlow يمكن أن يكون قابلاً للتغيير. إنه مشابه لـ LiveData، ولكنه مصمم خصيصًا للتفاعل مع واجهات المستخدم التفاعلية مثل Compose.Int: يعني أن StateFlow له قيمة من النوع Integer، وفي هذه الحالة يبدأ العداد من 0.(0): هذه هي القيمة الأولية المخصصة لحالة العداد. وهنا يبدأ العداد من الصفر. 2.حالة فال = _state.

asStateFlow()
asStateFlow ():تقوم هذه الوظيفة بتحويل MutableStateFlow إلى StateFlow غير قابل للتغيير.الغرض من هذه الخطوة هو إخفاء القدرة على تغيير الحالة خارج ViewModel، مما يضمن أنه لا يمكن تحديث الحالة إلا من ViewModel.

حتى يحصل تغير عندما الاستماع ،ولذلك نقوم بعمل اثنين الاولى mutable هذه تتغير معك ، ولكن الاخرى تكون private وهي التي تسمع منها وبتكون immutable.


استخدام ViewModel في Composable

@Composable
fun CounterController(viewModel: CounterViewModel = hiltViewModel()) {
    val state by viewModel.state.collectAsState()

    ShowDesign(
        counter = state.toString(),
        pluse = viewModel::add,
        minuse = viewModel::minus
    )
}

شرح الكود الخاص المسؤول عن controller

hiltViewModel(): يقوم Hilt تلقائيًا بعرض ViewModel وإتاحته للاستخدام في Composable
CollectAsState(): يُستخدم لإرجاع StateFlow إلى حالته الى Compose التي يمكن تتبعها.
وأخيرًا، نقوم بتمرير قيم الشرط (العداد) ووظائف التحكم (زائد وناقص) إلى الوظيفة التي تعرض المخطط.

تصميم الواجهه لعرض التصميم

@Composable
private fun ShowDesign(
    counter: String = "0",
    pluse: () -> Unit,
    minuse: () -> Unit
) {
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("مرحبا") },
                colors = TopAppBarDefaults.topAppBarColors(
                    containerColor = Color.Blue.copy(0.8f)
                )
            )
        },
        floatingActionButton = {
            FloatingActionButton(onClick = {}) {}
        }
    ) { paddingValues ->
        ConstraintLayout(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues) 
        ) {
            val (text, button1, button2) = createRefs()
            Column(
                modifier = Modifier.constrainAs(text) {
                    top.linkTo(parent.top)
                    start.linkTo(parent.start)
                    end.linkTo(parent.end)
                    bottom.linkTo(parent.bottom)
                },
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(text = counter)
                OutlinedButton(onClick = pluse) {
                    Text("+")
                }
                OutlinedButton(onClick = minuse) {
                    Text("-")
                }
            }
        }
    }
}

فوائد viewmodel و hit

ViewModel: يحافظ على حالة العداد ويمنع فقدان البيانات عند تدوير الشاشة.
Hilt: يسهل إدارة الاعتماديات مثل ViewModel بشكل تلقائي.
StateFlow: يقوم بتحديث واجهة المستخدم تلقائيًا عند تغيير الحالة.
تعليقات