Getting started

This page adds a first VIE screen, a counter, to an existing Compose Multiplatform project, such as one created by the Kotlin Multiplatform wizard. For an Android-only or JVM-only project, or to add the test helpers, see core:setup.adoc#jvm-or-android-only.

  1. Add VIE to the common dependencies of your shared module:

    build.gradle.kts
    kotlin {
        sourceSets {
            commonMain.dependencies {
                implementation("org.kodein.arch.vie:vie-mvi:0.1.0")
            }
        }
    }
  2. Declare the screen’s contract:

    CounterVIE.kt
    interface CounterVIE : VIE<Model, Intent, NoSignal> { (1)
    
        data class Model(val count: Int) : VIE.Model
    
        sealed interface Intent : VIE.Intent {
            data object Increment : Intent
            data object Reset : Intent
        }
    }
    1 Model and Intent are imported from CounterVIE itself, here and in the files below. This screen has nothing to signal, so it uses NoSignal, imported from org.kodein.arch.vie.VIE.NoSignal.

    The model is what the screen shows, and the intents are what the user can do.

  3. Implement it with a ViewModel:

    CounterVM.kt
    class CounterVM : CounterVIE, VIEViewModel<Model, Intent, NoSignal>(
        firstModel = Model(count = 0), (1)
    ) {
        override suspend fun process(intent: Intent) { (2)
            when (intent) {
                is Intent.Increment -> update { copy(count = count + 1) } (3)
                is Intent.Reset -> update { copy(count = 0) }
            }
        }
    }
    1 A screen always has a model, from the very start.
    2 Called for every intent the view posts, one at a time.
    3 The block receives the current model and returns the next one.

    CounterVM is an androidx.lifecycle.ViewModel, so it survives configuration changes on Android.

  4. Render it:

    Counter.kt
    @Composable
    fun Counter() {
        VIEView(newVM = { CounterVM() }) { model -> (1)
            Column {
                Text("Count: ${model.count}")
                Button(onClick = { post(Intent.Increment) }) { Text("+") } (2)
                Button(onClick = { post(Intent.Reset) }) { Text("Reset") }
            }
        }
    }
    1 Creates the ViewModel the first time, then recomposes with every new model.
    2 The view talks back by posting intents.
  5. Show it from your application’s root composable:

    App.kt
    @Composable
    fun App() {
        MaterialTheme {
            Counter()
        }
    }

That’s it! From here, The contract explains the three types of a screen, and how to pick between an Event and an Effect for its signals.