369 lines
6.9 KiB
Markdown
369 lines
6.9 KiB
Markdown
|
||
# 1. مشکل از کجا شروع شد؟
|
||
|
||
قبل از Jetpack Compose در اندروید، وقتی دادهای در برنامه تغییر میکرد باید **خودمان دستی UI را آپدیت میکردیم**.
|
||
|
||
مثال:
|
||
|
||
کاربر روی دکمه افزایش کلیک میکند → مقدار عدد تغییر میکند → باید TextView را هم تغییر دهیم.
|
||
|
||
در View های قدیمی:
|
||
|
||
```kotlin
|
||
count++
|
||
textView.text = count.toString()
|
||
```
|
||
|
||
یعنی برنامهنویس **باید یادش بماند UI را هم آپدیت کند**.
|
||
|
||
مشکلها:
|
||
|
||
- ممکن است فراموش کنیم UI را آپدیت کنیم
|
||
- کدها پیچیده میشوند
|
||
- نگهداری سخت میشود
|
||
|
||
---
|
||
|
||
# 2. راهحل Compose چیست؟
|
||
|
||
در Compose یک قانون مهم داریم:
|
||
|
||
**UI باید تابعی از State باشد**
|
||
|
||
یعنی:
|
||
|
||
```
|
||
UI = f(state)
|
||
```
|
||
|
||
اگر state تغییر کند → UI خودش دوباره ساخته میشود.
|
||
|
||
به این فرایند میگوییم:
|
||
|
||
**Recomposition**
|
||
|
||
---
|
||
|
||
# 3. State چیست؟
|
||
|
||
State یعنی:
|
||
|
||
**دادهای که اگر تغییر کند، UI باید تغییر کند.**
|
||
|
||
مثالها:
|
||
|
||
- مقدار counter
|
||
- متن داخل TextField
|
||
- وضعیت لودینگ
|
||
- لیست پیامها
|
||
- وضعیت لاگین کاربر
|
||
|
||
مثال ساده:
|
||
|
||
```kotlin
|
||
var count = 0
|
||
```
|
||
|
||
اگر مقدار `count` تغییر کند، UI باید تغییر کند.
|
||
|
||
اما Compose از تغییر این متغیر خبر ندارد.
|
||
|
||
پس باید از **State مخصوص Compose** استفاده کنیم.
|
||
|
||
---
|
||
|
||
# 4. mutableStateOf چیست؟
|
||
|
||
برای اینکه Compose بفهمد داده تغییر کرده باید از **State Object** استفاده کنیم.
|
||
|
||
یکی از مهمترین آنها:
|
||
|
||
```
|
||
mutableStateOf
|
||
```
|
||
|
||
مثال:
|
||
|
||
```kotlin
|
||
var count = mutableStateOf(0)
|
||
```
|
||
|
||
اینجا count یک **State** است.
|
||
|
||
برای دسترسی به مقدار:
|
||
|
||
```
|
||
count.value
|
||
```
|
||
|
||
مثال:
|
||
|
||
```kotlin
|
||
count.value++
|
||
```
|
||
|
||
وقتی مقدار تغییر کند:
|
||
|
||
Compose متوجه میشود → UI دوباره ساخته میشود.
|
||
|
||
---
|
||
|
||
# 5. استفاده از State در Compose
|
||
|
||
یک مثال ساده Counter:
|
||
|
||
```kotlin
|
||
@Composable
|
||
fun CounterScreen() {
|
||
|
||
var count = remember { mutableStateOf(0) }
|
||
|
||
Column {
|
||
|
||
Text(text = "Count: ${count.value}")
|
||
|
||
Button(onClick = {
|
||
count.value++
|
||
}) {
|
||
Text("Increase")
|
||
}
|
||
|
||
}
|
||
}
|
||
```
|
||
|
||
اتفاقی که میافتد:
|
||
|
||
1️⃣ صفحه ساخته میشود
|
||
2️⃣ مقدار count = 0
|
||
3️⃣ Text مقدار 0 را نشان میدهد
|
||
4️⃣ کاربر دکمه را میزند
|
||
5️⃣ مقدار count تغییر میکند
|
||
6️⃣ Compose تشخیص میدهد state تغییر کرده
|
||
7️⃣ UI دوباره اجرا میشود
|
||
|
||
این فرایند همان **Recomposition** است.
|
||
|
||
---
|
||
|
||
# 6. Recomposition چیست؟
|
||
|
||
**Recomposition یعنی اجرای دوباره Composable ها وقتی State تغییر میکند.**
|
||
|
||
در مثال قبل:
|
||
|
||
وقتی:
|
||
|
||
```
|
||
count.value++
|
||
```
|
||
|
||
اجرا میشود
|
||
|
||
Compose:
|
||
|
||
- متوجه تغییر state میشود
|
||
- Composable مربوطه را دوباره اجرا میکند
|
||
- UI جدید رسم میشود
|
||
|
||
نکته مهم:
|
||
|
||
Compose **کل صفحه را دوباره نمیسازد**
|
||
فقط بخشهایی که نیاز دارند دوباره اجرا میشوند.
|
||
|
||
---
|
||
|
||
# 7. remember چیست؟
|
||
|
||
یک مشکل مهم وجود دارد.
|
||
|
||
اگر این کد را بنویسیم:
|
||
|
||
```kotlin
|
||
var count = mutableStateOf(0)
|
||
```
|
||
|
||
در هر Recomposition مقدار دوباره **0 میشود**.
|
||
|
||
چرا؟
|
||
|
||
چون تابع دوباره اجرا میشود.
|
||
|
||
برای حل این مشکل از:
|
||
|
||
```
|
||
remember
|
||
```
|
||
|
||
استفاده میکنیم.
|
||
|
||
```kotlin
|
||
var count = remember { mutableStateOf(0) }
|
||
```
|
||
|
||
remember باعث میشود:
|
||
|
||
✅ مقدار بین recomposition ها حفظ شود.
|
||
|
||
---
|
||
|
||
# 8. روش بهتر: by delegation
|
||
|
||
نوشتن `.value` کمی آزاردهنده است.
|
||
|
||
روش بهتر:
|
||
|
||
```kotlin
|
||
var count by remember { mutableStateOf(0) }
|
||
```
|
||
|
||
حالا میتوانیم مستقیم استفاده کنیم:
|
||
|
||
```kotlin
|
||
count++
|
||
```
|
||
|
||
مثال کامل:
|
||
|
||
```kotlin
|
||
@Composable
|
||
fun CounterScreen() {
|
||
|
||
var count by remember { mutableStateOf(0) }
|
||
|
||
Column {
|
||
|
||
Text(text = "Count: $count")
|
||
|
||
Button(onClick = {
|
||
count++
|
||
}) {
|
||
Text("Increase")
|
||
}
|
||
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 9. جریان کامل اتفاقات
|
||
|
||
مراحل اجرای برنامه:
|
||
|
||
1️⃣ Composable اجرا میشود
|
||
2️⃣ State ساخته میشود
|
||
3️⃣ UI بر اساس State ساخته میشود
|
||
|
||
وقتی کاربر دکمه را میزند:
|
||
|
||
4️⃣ State تغییر میکند
|
||
5️⃣ Compose تغییر را تشخیص میدهد
|
||
6️⃣ Recomposition انجام میشود
|
||
7️⃣ UI جدید ساخته میشود
|
||
|
||
---
|
||
|
||
# 10. مثال Counter کامل
|
||
|
||
```kotlin
|
||
@Composable
|
||
fun CounterScreen() {
|
||
|
||
var count by remember { mutableStateOf(0) }
|
||
|
||
Column {
|
||
|
||
Text(
|
||
text = "Count: $count",
|
||
fontSize = 30.sp
|
||
)
|
||
|
||
Button(
|
||
onClick = { count++ }
|
||
) {
|
||
Text("Increase")
|
||
}
|
||
|
||
Button(
|
||
onClick = { count-- }
|
||
) {
|
||
Text("Decrease")
|
||
}
|
||
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
# 11. نکات مهم
|
||
|
||
✅ State دادهای است که UI به آن وابسته است.
|
||
|
||
✅ وقتی State تغییر کند → Recomposition اتفاق میافتد.
|
||
|
||
✅ برای تعریف State از `mutableStateOf` استفاده میکنیم.
|
||
|
||
✅ برای حفظ مقدار در Recomposition از `remember` استفاده میکنیم.
|
||
|
||
✅ بهتر است از `by` استفاده کنیم تا `.value` حذف شود.
|
||
|
||
---
|
||
|
||
# 12. تمرین برای دانشجویان
|
||
|
||
1️⃣ یک Counter بسازید که دکمههای زیر داشته باشد:
|
||
|
||
- Increase
|
||
- Decrease
|
||
- Reset
|
||
|
||
2️⃣ اگر مقدار Counter به **10 رسید** یک متن نمایش دهید:
|
||
|
||
```
|
||
Maximum reached!
|
||
```
|
||
|
||
3️⃣ اگر مقدار Counter **منفی شد** رنگ متن قرمز شود.
|
||
|
||
---
|
||
|
||
# 13. چالش فکری
|
||
|
||
اگر به جای State این کد را بنویسیم:
|
||
|
||
```kotlin
|
||
var count = 0
|
||
```
|
||
|
||
و در Button مقدار را زیاد کنیم:
|
||
|
||
```
|
||
count++
|
||
```
|
||
|
||
آیا UI تغییر میکند؟
|
||
|
||
چرا؟
|
||
|
||
---
|
||
|
||
پاسخ:
|
||
خیر. چون Compose از تغییر این متغیر خبر ندارد. بنابراین Recomposition اتفاق نمیافتد.
|
||
|
||
---
|
||
|
||
# جمعبندی
|
||
|
||
مهمترین مفاهیم Compose:
|
||
|
||
- State → داده قابل تغییر UI
|
||
- mutableStateOf → ساخت state
|
||
- remember → نگهداری مقدار در recomposition
|
||
- Recomposition → اجرای دوباره UI بعد از تغییر state
|
||
|
||
قانون اصلی Compose:
|
||
|
||
**UI تابعی از State است.**
|