Chanzmao ʕ•ᴥ•ʔ Bear Blog

Where and How to Convert to Flow in Android

From Data Sources to UI State: Best Practices for Reactive Streams

1. Data Layer (Repository / DataSource): Generating Cold Flows

class UserRepository(private val userDao: UserDao) {
    fun observeUser(userId: String): Flow<UserEntity?> {
        return userDao.observeUser(userId)
    }
}

2. Domain Layer (Use Case): Combining and Transforming Streams

class GetUserProfileUseCase(
    private val userRepository: UserRepository,
    private val settingsRepository: SettingsRepository
) {
    operator fun invoke(userId: String): Flow<UserProfile> {
        return combine(
            userRepository.observeUser(userId),
            settingsRepository.observeThemeSetting()
        ) { user, theme ->
            UserProfile(user = user, isDarkTheme = theme.isDark)
        }
    }
}

3. Presentation Layer (ViewModel): Converting to Hot StateFlow

class UserViewModel(
    getUserProfileUseCase: GetUserProfileUseCase,
    savedStateHandle: SavedStateHandle
) : ViewModel() {
    private val userId: String = checkNotNull(savedStateHandle["userId"])

    val uiState: StateFlow<UserUiState> = getUserProfileUseCase(userId)
        .map { profile -> UserUiState.Success(profile) }
        .catch { throwable -> emit(UserUiState.Error(throwable.message)) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = UserUiState.Loading
        )
}

4. UI Layer (Jetpack Compose): Collecting and Consuming

@Composable
fun UserScreen(viewModel: UserViewModel = viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()

    when (val state = uiState) {
        is UserUiState.Loading -> CircularProgressIndicator()
        is UserUiState.Success -> Text(text = "Hello, ${state.profile.user?.name}")
        is UserUiState.Error -> Text(text = "Error: ${state.message}")
    }
}

Summary

References

#Android #Flow #Jetpack Compose