Android Kaki

Build beautiful, usable products using required Components for Android.

Migrate from MVVM to MVI. Here is an evidence of how we… | by Kaaveh Mohamedi | March 2023


picture taken by meraji meraji ABOVE depart
MVVM diagram
Google Really helpful One-Method Knowledge Stream Structure
  1. Intent-based interplay between UI and ViewModel has extra restrictions to adjust to sole duty.
  2. All strategies of ViewModel turn out to be personal. Because of this Encapsulation (one of many ideas of OOP) has been noticed extra. Any longer, you’re much less involved with the precise implementation of the ViewModel; You simply want to fireside the intent from the UI (So stunning! 😍).
  3. The second good thing about Intent-based interplay is rising Abstraction (one other precept of OOP) within the communication between UI and ViewModel.
  4. In the long run, I discover MVI extra appropriate to make use of Jetpack Compose than MVVM. I do know that you may have a number of old-style state of MVVM’s ViewModel and go ViewModel’s methodology calls as lambda to Composable capabilities, however in the case of MVI, why pressure your self to make use of MVVM?! ?😁
Me after getting acquainted with MVI! 😍🥰
interface NewsListContract :
UnidirectionalViewModelNewsListContract.Impact> {
knowledge class State(
val information: Record = listOf(),
val refreshing: Boolean = false,
val showFavoriteList: Boolean = false,
)


sealed class Occasion {
knowledge class OnFavoriteClick(val information: Information) : Occasion()
knowledge class OnGetNewsList(val showFavoriteList: Boolean) : Occasion()
knowledge class OnSetShowFavoriteList(val showFavoriteList: Boolean) : Occasion()
object OnRefresh: Occasion()
object OnBackPressed : Occasion()
knowledge class ShowToast(val message: String) : Occasion()
}


sealed class Impact {
object OnBackPressed : Impact()
knowledge class ShowToast(val message: String) : Impact()
}
}

interface UnidirectionalViewModel {
val state: StateFlow
val impact: SharedFlow
enjoyable occasion(occasion: EVENT)
}
  1. A variable to maintain Standing
  2. A move for Operate
  3. And overwrite occasion perform
@HiltViewModel
class NewsListViewModel @Inject constructor(
personal val getNewsUseCase: GetNewsUseCase,
personal val getFavoriteNewsUseCase: GetFavoriteNewsUseCase,
personal val toggleFavoriteNewsUseCase: ToggleFavoriteNewsUseCase,
) : NewsListContract {
personal val mutableState = MutableStateFlow(NewsListContract.State())
override val state: StateFlow =
mutableState.asStateFlow()


personal val effectFlow = MutableSharedFlow()
override val impact: SharedFlow =
effectFlow.asSharedFlow()


override enjoyable occasion(occasion: NewsListContract.Occasion) = when (occasion) {
is NewsListContract.Occasion.OnSetShowFavoriteList ->
onSetShowFavoriteList(showFavoriteList = occasion.showFavoriteList)
is NewsListContract.Occasion.OnGetNewsList ->
getData(showFavoriteList = mutableState.worth.showFavoriteList)
is NewsListContract.Occasion.OnFavoriteClick ->
onFavoriteClick(information = occasion.information)
NewsListContract.Occasion.OnRefresh -> getData(isRefreshing = true)
NewsListContract.Occasion.OnBackPressed -> onBackPressed()
is NewsListContract.Occasion.ShowToast -> showToast(occasion.message)
}


personal enjoyable onSetShowFavoriteList(showFavoriteList: Boolean) {
mutableState.replace {
it.copy(showFavoriteList = showFavoriteList)
}
}


personal enjoyable getData(
isRefreshing: Boolean = false,
showFavoriteList: Boolean = false,
) {
if (isRefreshing)
mutableState.replace {
NewsListContract.State(
refreshing = true,
)
}
viewModelScope.launch {
if (showFavoriteList)
getFavoriteNews()
else
getNewsList()
}
}


personal droop enjoyable getNewsList() = getNewsUseCase()
.catch { exception ->
mutableBaseState.replace {
BaseContract.BaseState.OnError(
errorMessage = exception.localizedMessage
?: "An surprising error occurred."
)
}
}
.onEach { outcome ->
mutableState.replace {
NewsListContract.State(information = outcome)
}
}
.launchIn(viewModelScope)


personal enjoyable getFavoriteNews() = getFavoriteNewsUseCase()
.onEach { newList ->
mutableState.replace {
it.copy(information = newList)
}
}.launchIn(viewModelScope)


personal enjoyable onFavoriteClick(information: Information) {
viewModelScope.launch(Dispatchers.IO) {
toggleFavoriteNewsUseCase(information)
}
}


personal enjoyable onBackPressed() {
viewModelScope.launch {
effectFlow.emit(NewsListContract.Impact.OnBackPressed)
}
}


personal enjoyable showToast(message: String) {
viewModelScope.launch {
effectFlow.emit(
NewsListContract.Impact.ShowToast(message = message)
)
}
}


}

  1. Knowledge in Standing go to all needed Composable capabilities
  2. The Operate move begins to be collected
  3. And occasion the perform known as wherever wanted and through the corresponding occasion
@Composable
enjoyable NewsListRoute(
viewModel: NewsListViewModel = hiltViewModel(),
showFavoriteList: Boolean = false,
onNavigateToDetailScreen: (information: Information) -> Unit,
) {
// Implementation of `use` within the notice part
val (state, impact, occasion) = use(viewModel = viewModel)
val exercise = LocalContext.present as? Exercise
/*
Get preliminary knowledge
Do not use the init block in ViewModel as a lot as doable
This makes testing tougher!
*/
LaunchedEffect(key1 = Unit) {
occasion.invoke(
NewsListContract.Occasion.OnSetShowFavoriteList(
showFavoriteList = showFavoriteList,
)
)
occasion.invoke(
NewsListContract.Occasion.OnGetNewsList(
showFavoriteList = showFavoriteList,
)
)
}


// Implementation of `collectInLaunchedEffect` within the notice part
impact.collectInLaunchedEffect {
when (it) {
NewsListContract.Impact.OnBackPressed -> {
exercise?.onBackPressed()
}
is NewsListContract.Impact.ShowToast -> {
Toast.makeText(exercise, it.message, Toast.LENGTH_LONG).present()
}
}
}


NewsListScreen(
newsListState = state,
onNavigateToDetailScreen = onNavigateToDetailScreen,
onFavoriteClick = { information ->
occasion.invoke(NewsListContract.Occasion.OnFavoriteClick(information = information))
},
onRefresh = {
occasion.invoke(NewsListContract.Occasion.OnRefresh)
},
onBackPressed = {
occasion.invoke(NewsListContract.Occasion.OnBackPressed)
},
showToast = { message ->
occasion.invoke(NewsListContract.Occasion.ShowToast(message))
},
)
}


@OptIn(ExperimentalMaterialApi::class)
@Composable
personal enjoyable NewsListScreen(
newsListState: NewsListContract.State,
onNavigateToDetailScreen: (information: Information) -> Unit,
onFavoriteClick: (information: Information) -> Unit,
onRefresh: () -> Unit,
onBackPressed: () -> Unit,
showToast: (message: String) -> Unit,
) {
val refreshState = rememberPullRefreshState(
refreshing = newsListState.refreshing,
onRefresh = onRefresh,
)


Field(
modifier = Modifier
.fillMaxWidth()
.pullRefresh(refreshState)
) {
AnimatedVisibility(
seen = !newsListState.refreshing,
enter = fadeIn(),
exit = fadeOut(),
) {
Row {
Button(onClick = {
onBackPressed()
}) {
Textual content(textual content = "onBackPressed")
}


Spacer(modifier = Modifier.width(16.dp))


Button(onClick = {
showToast(message = "Hiiiiiii!")
}) {
Textual content(textual content = "Present Toast")
}
}
LazyColumn(modifier = Modifier.fillMaxWidth()) {
gadgets(newsListState.information) { information ->
NewsListItem(
information = information,
onItemClick = {
onNavigateToDetailScreen(information)
},
onFavoriteClick = {
onFavoriteClick(information)
}
)
}
}
}
PullRefreshIndicator(
newsListState.refreshing,
refreshState,
Modifier.align(Alignment.TopCenter)
)
}
}


@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@ThemePreviews
@Composable
personal enjoyable NewsListScreenPrev(
@PreviewParameter(NewsListStateProvider::class)
newsListState: NewsListContract.State
) {
ComposeNewsTheme {
Scaffold {
NewsListScreen(
newsListState = newsListState,
onNavigateToDetailScreen = {},
onFavoriteClick = {},
onRefresh = {},
onBackPressed = {},
showToast = {},
)
}
}
}

@Composable
inline enjoyable use(
viewModel: UnidirectionalViewModel,
): StateDispatchEffect {
val state by viewModel.state.collectAsStateWithLifecycle()
val dispatch: (EVENT) -> Unit = { occasion ->
viewModel.occasion(occasion)
}


return StateDispatchEffect(
state = state,
effectFlow = viewModel.impact,
dispatch = dispatch,
)
}


knowledge class StateDispatchEffect(
val state: STATE,
val dispatch: (EVENT) -> Unit,
val effectFlow: SharedFlow,
)

@Suppress("ComposableNaming")
@Composable
enjoyable SharedFlow.collectInLaunchedEffect(perform: droop (worth: T) -> Unit) {
val sharedFlow = this
LaunchedEffect(key1 = sharedFlow) {
sharedFlow.collectLatest(perform)
}
}

John Wick: Chapter 4 (FREE) FULLMOVIE The Super Mario Bros Movie avatar 2 Where To Watch Creed 3 Free At Home Knock at the Cabin (2023) FullMovie Where To Watch Ant-Man 3 and the Wasp: Quantumania Cocaine Bear 2023 (FullMovie) Scream 6 Full Movie

Supply hyperlink

Updated: April 4, 2023 — 12:47 am

Leave a Reply

Your email address will not be published. Required fields are marked *

androidkaki.com © 2023 Android kaki