package com.example.hiltdemo
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Inject
import javax.inject.Singleton
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import androidx.hilt.navigation.compose.hiltViewModel
// Data class representing a user
data class User(val name: String, val email: String)
// Repository interface
interface UserRepository {
suspend fun getUser(): User
}
// Repository implementation
class UserRepositoryImpl @Inject constructor() : UserRepository {
override suspend fun getUser(): User {
// Simulate network or database delay
kotlinx.coroutines.delay(1000)
return User(name = "Alice Johnson", email = "alice@example.com")
}
}
// Hilt module to provide UserRepository
@Module
@InstallIn(SingletonComponent::class)
object UserRepositoryModule {
@Provides
@Singleton
fun provideUserRepository(): UserRepository = UserRepositoryImpl()
}
// ViewModel with injected UserRepository
@HiltViewModel
class UserViewModel @Inject constructor(
private val userRepository: UserRepository
) : ViewModel() {
private val _user = MutableStateFlow<User?>(null)
val user: StateFlow<User?> = _user
init {
loadUser()
}
fun loadUser() {
viewModelScope.launch {
_user.value = null // Show loading
val fetchedUser = userRepository.getUser()
_user.value = fetchedUser
}
}
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
Surface(modifier = Modifier.padding(16.dp)) {
UserProfileScreen()
}
}
}
}
}
@Composable
fun UserProfileScreen(viewModel: UserViewModel = hiltViewModel()) {
val userState by viewModel.user.collectAsState()
Column {
if (userState == null) {
Text(text = "Name: Loading...")
Text(text = "Email: Loading...")
} else {
Text(text = "Name: ${userState!!.name}")
Text(text = "Email: ${userState!!.email}")
}
Button(onClick = { viewModel.loadUser() }, modifier = Modifier.padding(top = 16.dp)) {
Text("Refresh")
}
}
}
This solution uses Hilt to inject the UserRepository into the UserViewModel. The UserRepositoryImpl simulates fetching user data with a delay. The UserViewModel exposes a StateFlow of User? to represent loading and loaded states.
The UserProfileScreen composable gets the ViewModel instance via hiltViewModel(), observes the user state, and shows "Loading..." text while data is null. When data is loaded, it displays the user's name and email. The Refresh button calls loadUser() to reload data.
This setup demonstrates dependency injection with Hilt, ViewModel state management, and Compose UI updates reacting to state changes.