Add to build.gradle
or build.gradle.kts
:
- root project gradle
build.gradle.kts
:
allprojects {
repositories {
maven(url = "https://jitpack.io")
}
}
- in your application module
build.gradle
orbuild.gradle.kts
:
plugins{
id("com.google.devtools.ksp") version ("1.8.0-1.0.9")
}
dependencies {
implementation("com.github.triandamai.mvi:ui:<version>")
implementation("com.github.triandamai.mvi:processor:<version>")
ksp("com.github.triandamai.mvi:processor:<version>")
}
@Navigation(
route="first-page",
viewModel=ListQuizViewModel::class
)
@Composable
fun DetailQuizScreen(
uiContract: UIContract<ListQuizState, ListQuizAction>
) = UiWrapper(uiContract) {
Button(
onClick={
//navigate to second page with params
navigator.navigate("second-page","World")
}
){ Text("Send to Second Page") }
}
@Navigation(
route="second-page",
viewModel=DetailQuizViewModel::class
)
@Argument(name="quizName", type=NavType.StringType)
@Composable
fun DetailQuizScreen(
uiContract: UIContract<DetailQuizState,DetailQuizAction>
) = UiWrapper(uiContract) {
LaunchedEffect(this){
val arg = navigator.arguments.get<String>("quizName")
commit{copy(message=arg)}
}
//when ViewModel send 'Effect'
UseEffect(
key = state.effect,
onDispose = { copy(effect = ListQuizEffect.Nothing) }, //after effect triggered then the effect reset with this line
block = {
when (this) {
ListQuizEffect.Nothing -> Unit
is ListQuizEffect.ShowToast -> {
//todo
}
}
}
)
Text("Hello ${state.message}")
}
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val uiController = rememberUIController(event = EventListener())
QuizTheme {
NavHost(controller,"first-page") {
//add this line
//this file are generated from annotation processor
quizComponent(uiController = uiController)
}
}
}
}
}
@HiltViewModel
class ListQuizViewModel @Inject constructor(
private val getListQuizUseCase: GetListQuizUseCase
) : MviViewModel<ListQuizState, ListQuizAction>(
ListQuizState(),
) {
init {
getListQuiz()
}
private fun getListQuiz() = async {
getListQuizUseCase()
.onEach(
loading = {},
error = { _, _ -> },
success = {},
empty = {}
)
}
//handle event from screen
// when dispatch invoked
override fun onAction(action: ListQuizAction) {
when (action) {
ListQuizAction.Nothing -> {
}
}
}
}
State:
@Immutable
@Parcelize
data class ListQuizState(
val effect:ListQuizEffect=ListQuizEffect.Nothing
)
Action:
sealed interface ListQuizAction {
object Nothing : ListQuizAction
}
Effect:
sealed interface ListQuizEffect {
object Nothing : ListQuizEffect
data class ShowToast(val message:String) : ListQuizEffect
}
This project doesn't mean to replace existing architecture, and work seamless with the existing:
- Reduce boiler plate
- Better testing (Screen/Page can be tested separately from ViewModel)
MIT License
Copyright (c) 2023 trian damai
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.