Skip to content

Instantly share code, notes, and snippets.

View Skyyo's full-sized avatar
🇺🇦

Denis Rudenko Skyyo

🇺🇦
View GitHub Profile
@Composable
fun InputValidationScreen(viewModel: InputValidationAutoViewModel = hiltViewModel()) {
val events = remember(viewModel.events, lifecycleOwner) {..}
val name by viewModel.name.collectAsStateWithLifecycle()
val creditCardNumber by viewModel.creditCardNumber.collectAsStateWithLifecycle()
val areInputsValid by viewModel.areInputsValid.collectAsStateWithLifecycle()
LaunchedEffect(Unit) {
events.collect { event ->
handle[NAME] = name.value.copy(value = input, errorId = errorId)
@Parcelize
data class InputWrapper(
val value: String = "",
val errorId: Int? = null
) : Parcelable
fun <T> SavedStateHandle.getStateFlow(
scope: CoroutineScope,
key: String,
initialValue: T
): MutableStateFlow<T> {
val liveData = getLiveData(key, initialValue)
val stateFlow = MutableStateFlow(initialValue)
val observer = Observer<T> { value -> if (value != stateFlow.value) stateFlow.value = value }
liveData.observeForever(observer)
object InputValidator {
fun getNameErrorIdOrNull(input: String): Int? {
return when {
input.length < 2 -> R.string.name_too_short
//ideally provide different error messages for different errors
else -> null
}
}
@HiltViewModel
class InputValidationAutoViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val name = handle.getStateFlow(NAME, InputWrapper())
val creditCardNumber = handle.getStateFlow(CREDIT_CARD_NUMBER, InputWrapper())
val areInputsValid = combine(name, creditCardNumber) { name, cardNumber ->
name.value.isNotEmpty() && name.errorId == null && cardNumber.value.isNotEmpty() && cardNumber.errorId == null
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000L), false)
@Composable
fun LifecycleAwareCurrencyPriceCard(
currencyPrice: CurrencyPrice,
currencyPriceUpdateFlow: Flow<Int>,
onCurrencyUpdated: (progress: Int) -> Unit,
onDisposed: () -> Unit,
) {
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycleAwareCurrencyPriceFlow = remember(currencyPriceUpdateFlow, lifecycleOwner) {
currencyPriceUpdateFlow.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.STARTED)
@Composable
fun LifecycleAwareCurrenciesScreen(viewModel: LifecycleAwareCurrenciesViewModel) {
val currencyPrices by viewModel.currencyPrices.collectAsStateWithLifecycle()
LazyColumn {
itemsIndexed(currencyPrices, { _, item -> item.id }) { index, currencyPrice ->
LifecycleAwareCurrencyPriceCard(
currencyPrice = currencyPrice,
currencyPriceUpdateFlow = viewModel.provideCurrencyUpdateFlow(),
onDisposed = { viewModel.onDisposed(index) },
onCurrencyUpdated = { newPrice -> viewModel.onCurrencyUpdated(newPrice, index) })
class LifecycleAwareCurrenciesViewModel : ViewModel() {
private val _currencyPrices = MutableStateFlow(listOf<CurrencyPrice>())
val currencyPrices: StateFlow<List<CurrencyPrice>> get() = _currencyPrices
init {
getCurrencyPrices()
}
private fun getCurrencyPrices() {} //same as before
private suspend fun observePriceUpdateFlow(index: Int, currencyPriceUpdateFlow: Flow<Int>) {
currencyPriceUpdateFlow.collect { newPrice ->
val newFluctuation = when {
newPrice > _currencyPrices.value[index].price -> PriceFluctuation.UP
else -> PriceFluctuation.DOWN
}
val updatedCurrencyPrice = _currencyPrices.value[index].copy(
price = newPrice,
priceFluctuation = newFluctuation
)