Skip to content

Instantly share code, notes, and snippets.

@mahdiPourkazemi
Last active August 7, 2025 08:44
Show Gist options
  • Save mahdiPourkazemi/dbd2f051f36624bcb92a873dc102b416 to your computer and use it in GitHub Desktop.
Save mahdiPourkazemi/dbd2f051f36624bcb92a873dc102b416 to your computer and use it in GitHub Desktop.
LaunchedEffect vs rememberCoroutineScope in compose
//########LaunchedEffect####################
// first load in page: init vs Launched Effect
@Composable
fun MyScreen(userId: String) {
LaunchedEffect(userId) {
// هر بار userId تغییر کنه اجرا میشه
val user = userRepository.getUser(userId)
println(user.name)
}
}
//##############################################################
LaunchedEffect(Unit) {
val savedToken = dataStore.getToken()
if (savedToken != null) {
navigateToHome()
}
}
//##############################################################
// if data key come from user use the launchedEffect like this example if not use init{} in viewMOdel
// ViewModel - مدیریت State و Business Logic
@HiltViewModel
class UserProfileViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(UserProfileUiState())
val uiState = _uiState.asStateFlow()
fun loadUser(userId: String) {
viewModelScope.launch {
_uiState.value = _uiState.value.copy(isLoading = true)
try {
val user = userRepository.getUser(userId)
_uiState.value = _uiState.value.copy(
user = user,
isLoading = false,
error = null
)
} catch (e: Exception) {
_uiState.value = _uiState.value.copy(
isLoading = false,
error = e.message ?: "خطای نامشخص"
)
}
}
}
}
// UI State Data Class
data class UserProfileUiState(
val user: User? = null,
val isLoading: Boolean = false,
val error: String? = null
)
// User Data Class
data class User(
val id: String,
val name: String,
val email: String,
val avatarUrl: String?
)
// Composable - فقط UI و Reactive Calls
@Composable
fun UserProfileScreen(
userId: String,
viewModel: UserProfileViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
// Reactive call - هر بار userId تغییر کند، دوباره لود می‌شود
LaunchedEffect(userId) {
viewModel.loadUser(userId)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
when {
uiState.isLoading -> {
CircularProgressIndicator()
Text("در حال بارگذاری...")
}
uiState.error != null -> {
Text(
text = "خطا: ${uiState.error}",
color = MaterialTheme.colorScheme.error
)
Button(
onClick = { viewModel.loadUser(userId) }
) {
Text("تلاش مجدد")
}
}
uiState.user != null -> {
UserProfileContent(user = uiState.user)
}
}
}
}
@Composable
private fun UserProfileContent(user: User) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
// Avatar
AsyncImage(
model = user.avatarUrl,
contentDescription = "تصویر پروفایل ${user.name}",
modifier = Modifier
.size(100.dp)
.clip(CircleShape),
placeholder = painterResource(R.drawable.ic_person)
)
// Name
Text(
text = user.name,
style = MaterialTheme.typography.headlineMedium,
fontWeight = FontWeight.Bold
)
// Email
Text(
text = user.email,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
// User ID (for demo)
Text(
text = "شناسه: ${user.id}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.outline
)
}
}
// استفاده در Navigation
@Composable
fun UserProfileRoute(
userId: String,
onBackClick: () -> Unit
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("پروفایل کاربر") },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.Default.ArrowBack, "بازگشت")
}
}
)
}
) { paddingValues ->
Box(modifier = Modifier.padding(paddingValues)) {
UserProfileScreen(userId = userId)
}
}
}
//##############################################################
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
val result = longRunningTask()
println(result)
}
}) {
Text("Click me")
}
//##############################################################
var isLoading by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Button(
onClick = {
if (!isLoading) {
isLoading = true
scope.launch {
val result = longRunningTask()
println(result)
isLoading = false
}
}
},
enabled = !isLoading
) {
Text(if (isLoading) "Loading..." else "Click me")
}
//##############################################################
val scope = rememberCoroutineScope()
var job by remember { mutableStateOf<Job?>(null) }
Button(onClick = {
if (job?.isActive != true) {
job = scope.launch {
longRunningTask()
}
}
}) {
Text("Click me")
}
//##############################################################
@Composable
fun AsyncButton() {
var isLoading by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope()
Button(
onClick = {
if (!isLoading) {
isLoading = true
scope.launch {
try {
val result = longRunningTask()
println("Result: $result")
} catch (e: Exception) {
println("Error: ${e.message}")
// Handle error (show toast, snackbar, etc.)
} finally {
isLoading = false
}
}
}
},
enabled = !isLoading
) {
if (isLoading) {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
color = MaterialTheme.colorScheme.onPrimary
)
Text("Loading...")
}
} else {
Text("Click me")
}
}
}
//##############################################################
// روش حرفه‌ای‌تر با ViewModel
@Composable
fun AsyncButtonWithViewModel(viewModel: MyViewModel) {
val uiState by viewModel.uiState.collectAsState()
Button(
onClick = { viewModel.performLongRunningTask() },
enabled = uiState !is UiState.Loading
) {
when (uiState) {
is UiState.Loading -> {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp
)
Text("Loading...")
}
}
is UiState.Error -> Text("Retry")
else -> Text("Click me")
}
}
}
// ViewModel مربوطه
class MyViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Idle)
val uiState = _uiState.asStateFlow()
fun performLongRunningTask() {
if (_uiState.value is UiState.Loading) return
viewModelScope.launch {
_uiState.value = UiState.Loading
try {
val result = longRunningTask()
_uiState.value = UiState.Success(result)
} catch (e: Exception) {
_uiState.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
}
sealed class UiState {
object Idle : UiState()
object Loading : UiState()
data class Success(val data: String) : UiState()
data class Error(val message: String) : UiState()
}
suspend fun longRunningTask(): String {
delay(3000) // شبیه‌سازی کار زمان‌بر
return "Task completed!"
}
@mahdiPourkazemi
Copy link
Author

Kotlin Compose State Management

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment