Best Premium Templates For App and Software Downloading Site. Made By HIVE-Store

Android App Development

Stay ahead with the latest tools, trends, and best practices in Android development

MVVM vs MVI in Jetpack Compose

MVVM vs MVI in Jetpack Compose - Coding Bihar
MVVM vs MVI in Jetpack Compose

MVVM vs MVI in Jetpack Compose – Which One Should You Use?

Jetpack Compose has completely changed how we build Android UIs. No more XML. Just pure Kotlin code to declare your UI. But with great UI power comes great architecture confusion!

The big question:

❓ Should you use MVVM or MVI with Jetpack Compose?

Let’s break it down like friends talking over chai – with real-world examples, code patterns, and a sprinkle of developer wisdom. ๐Ÿง 

Learn how to convert a Website into Android App Using Jetpack Compose

๐Ÿ“ฆ What Is MVVM?

MVVM stands for Model-View-ViewModel. It's been the go-to architecture for Android devs for years.

Model: The data layer (Room DB, API, etc.)
View: The UI (Jetpack Compose screen)
ViewModel: Handles UI logic, exposes data using StateFlow, LiveData, etc.

๐Ÿง  How It Works in Compose:

@Composable
fun TodoScreen(viewModel: TodoViewModel = hiltViewModel()) {
    val todos by viewModel.todoList.collectAsState()

    LazyColumn {
        items(todos) { todo ->
            Text(todo.title)
        }
    }
}
Here, your ViewModel pushes state and the Composable observes it. Simple. Clean. Easy to test.

JETPACK COMPOSE STORY READER APP An app that speak

๐Ÿ” What Is MVI?

MVI stands for Model-View-Intent. It’s inspired by reactive and functional programming (hello Redux fans!).

Model: The full UI state
View: Emits Intents (user actions)
Intent: Triggers an update to the Model
State: A single source of truth, often represented by a data class

๐Ÿง  How It Works in Compose:
data class TodoState(
    val todos: List = emptyList(),
    val isLoading: Boolean = false,
    val error: String? = null
)

sealed class TodoIntent {
    object LoadTodos : TodoIntent()
    data class AddTodo(val title: String) : TodoIntent()
}
Composable observes TodoState, sends TodoIntent, and the ViewModel processes it via a reducer-style method.

๐Ÿงช Key Differences

Feature MVVMMVI
State HandlingMultiple sources (LiveData, StateFlow)Single immutable state
Unidirectional FlowOptionalStrict
Complex UI StateCan get messyCentralized in one class
TestingViewModel is easy to unit testSlightly more boilerplate but predictable
Learning CurveEasy for beginnersTakes time to grasp reducer pattern
Tooling SupportWorks well with Jetpack ecosystemNo native tooling but clean logic

๐Ÿ’ฅ Real-World Analogy

Imagine you’re running a tea shop.

MVVM:

  • You (UI) talk to your staff (ViewModel).
  • Your staff manages orders and tells the kitchen (Model).
  • They give you updates when tea is ready.
  • Simple, but if the tea machine (state) is acting weird, you might get confused where the delay happened.

MVI:

  • You write everything in a logbook (Intent: MakeTea, ServeTea, etc.)
  • The logbook decides how to react, step by step.
  • The state of the tea shop is always in one place.
  • More setup, but very predictable and traceable.

✅ When to Use What?

Use CaseRecommendation
๐Ÿš€ Small to Medium AppsMVVM is perfect
๐Ÿ”„ Complex UI State (forms, tabs, dynamic UI)MVI gives better control
๐Ÿงช You want testability and time travel debuggingMVI wins here
๐Ÿง  Team already knows MVVMStick with MVVM for faster delivery
๐Ÿงฑ You love functional programmingTry MVI—you’ll enjoy it!

⚔️ My Personal Verdict (as a dev)

If you’re building apps like Notes, Recipes, or E-commerce, MVVM is quick, intuitive, and works beautifully with Jetpack Compose.

But if you’re working on something like a real-time dashboard, game UI, or forms with lots of branching logic, MVI helps you avoid future headaches.

Use the right tool for the right job. Architecture isn’t religion—it’s a strategy. ๐Ÿงฉ

๐ŸŽฏ Final Thoughts

Jetpack Compose doesn’t force one pattern over another. That’s its beauty.

Whether you go MVVM or MVI, consistency and clean state management are what matter most.

How to use Firebase Google Sign in?

Let’s Build a Counter App using MVI

We understood what MVI (Model-View-Intent) is and how it brings structure and predictability to our Jetpack Compose apps.
Now, let’s build our first real app using MVI—a clean, reactive Counter App with increment, decrement, and reset features.

๐ŸŽฏ What We'll Build

A simple counter app with three buttons:
  • ➕ Increase Count
  • ➖ Decrease Count
  • ๐Ÿ” Reset to Zero

We'll use:

  • Immutable State
  • Sealed Intent classes
  • A ViewModel to process intents and update state
  • Jetpack Compose for the UI

๐Ÿง  MVI Components Recap

Before diving into code, here's how MVI works:
ComponentRole
IntentUser actions (Button clicked, Text changed)
StateFull representation of the UI
ViewModelProcesses intents, updates state
View (UI)Reads state, sends intents

1. ๐Ÿ“ฆ Create the UI State

data class CounterState(
    val count: Int = 0
)

2. ๐Ÿšฆ Define Intents

sealed class CounterIntent {
    data object Increment : CounterIntent()
    data object Decrement : CounterIntent()
    data object Reset : CounterIntent()
}

3. ๐Ÿง  ViewModel: The Reducer and State Flow

package com.example.codingcompose

import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

class CounterViewModel : ViewModel() {

    private val _state = MutableStateFlow(CounterState())
    val state: StateFlow = _state.asStateFlow()

    fun onIntent(intent: CounterIntent) {
        when (intent) {
            is CounterIntent.Increment -> {
                _state.value = _state.value.copy(count = _state.value.count + 1)
            }
            is CounterIntent.Decrement -> {
                _state.value = _state.value.copy(count = _state.value.count - 1)
            }
            is CounterIntent.Reset -> {
                _state.value = _state.value.copy(count = 0)
            }
        }
    }
}

4. ๐ŸŽจ UI in Jetpack Compose

package com.example.codingcompose

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun CounterScreen(viewModel: CounterViewModel = viewModel()) {
    val state by viewModel.state.collectAsState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(32.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Count: ${state.count}", fontSize = 32.sp, 
            fontWeight = FontWeight.Bold)

        Spacer(modifier = Modifier.height(24.dp))

        Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
            Button(onClick = { viewModel.onIntent(CounterIntent.Decrement) }) {
                Text("➖")
            }
            Button(onClick = { viewModel.onIntent(CounterIntent.Reset) }) {
                Text("๐Ÿ”")
            }
            Button(onClick = { viewModel.onIntent(CounterIntent.Increment) }) {
                Text("➕")
            }
        }
    }
}

๐ŸŽ‰ Result

A smooth, reactive counter app that follows unidirectional flow with centralized state—this is the power of MVI.
Jetpack Compose Counter App using MVI

Jetpack Compose Counter App using MVVM

Let’s rebuild the same Counter App using MVVM (Model-View-ViewModel), so you and your readers can compare both patterns clearly.

๐Ÿ“ฆ MVVM Architecture 

LayerResponsibility
ModelThe data (we won’t need a backend for this simple app)
ViewModelHolds business logic and exposes UI state
View (UI)Displays UI and observes state from ViewModel

✅ MVVM Counter App in Jetpack Compose

1. ๐Ÿง  ViewModel with MutableInState

package com.example.counterapp

import androidx.compose.runtime.IntState
import androidx.compose.runtime.mutableIntStateOf
import androidx.lifecycle.ViewModel

class CounterMvvmViewModel : ViewModel() {
    private val _count = mutableIntStateOf(0)
    val count: IntState = _count

    fun increment() {
        _count.intValue++
    }

    fun decrement() {
        _count.intValue--
    }

    fun reset() {
        _count.intValue = 0
    }
}
๐Ÿ” Simpler and more direct than MVI—each function mutates the state.

2. ๐ŸŽจ Composable UI

package com.example.counterapp

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun CounterScreen(viewModel: CounterMvvmViewModel = viewModel()) {
    val count = viewModel.count.intValue

    Column(
        modifier = Modifier
            .systemBarsPadding()
            .fillMaxSize()
            .padding(32.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Count: $count", fontSize = 32.sp)
        Spacer(modifier = Modifier.height(24.dp))
        Row(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
            ExtendedFloatingActionButton(onClick = { viewModel.decrement() }) {
                Text("➖")
            }
            ExtendedFloatingActionButton(onClick = { viewModel.reset() }) {
                Text("๐Ÿ” Reset")
            }
            ExtendedFloatingActionButton(onClick = { viewModel.increment() }) {
                Text("➕")
            }
        }
    }
}

OUTPUT:

Jetpack Compose Counter App using MVVM

๐ŸŽฏ My Final Thoughts

  1. Use MVVM when building quick, readable, smaller UIs.
  2. Use MVI when scaling to complex state, side-effects, or reactive patterns.

Special Message

Welcome to Coding