NOTE:
- State in a value in or pass to composable, where changes of the value would update the UI
- We store state in ViewModel, and pass it into composable, in order to make the composable stateless and easier to test
- Using MutableState is the most convinient, but we could use LiveData, Flow or RxJava2
MutableState
ViewModel
class MainViewModel : ViewModel() { var currentUser by mutableStateOf<FirebaseUser?>(null) private set @JvmName("assignCurrentUser") fun setCurrentUser(user: FirebaseUser?) { currentUser = user }}
Composable
@Composablefun UserProfileScreen(viewModel: MainViewModel, onSignIn: () -> Unit, onSignOut: () -> Unit) { val currentUser = viewModel.currentUser Column(modifier = Modifier.padding(16.dp)) { if (currentUser == null) { Button(onClick = { onSignIn() }){ Text(text = "Sign in with Google") } } else { Text(text = "Welcome, ${currentUser.displayName}") Button(onClick = { onSignOut() } ) { Text(text = "Sign out") } } }}
LiveData
ViewModel
class MainViewModel : ViewModel() { private var _currentUser: MutableLiveData<FirebaseUser?> = MutableLiveData(null) val currentUser: LiveData<FirebaseUser?> = _currentUser fun setCurrentUser(user: FirebaseUser?) { _currentUser.value = user }}
Composable
@Composablefun UserProfileScreen(viewModel: MainViewModel, onSignIn: () -> Unit, onSignOut: () -> Unit) { val currentUser: FirebaseUser? by viewModel.currentUser.observeAsState(null) Column(modifier = Modifier.padding(16.dp)) { if (currentUser == null) { Button(onClick = { onSignIn() }){ Text(text = "Sign in with Google") } } else { Text(text = "Welcome, ${currentUser!!.displayName}") Button(onClick = { onSignOut() } ) { Text(text = "Sign out") } } }}
References: