diff --git a/README.md b/README.md
index 4ed789298c..14b40adda7 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Preview functionality (check your application UI without building/running it) fo
* [compose-bird](examples/web-compose-bird) - A flappy bird clone using Compose for Web
* [notepad](examples/notepad) - Notepad, using the new experimental Composable Window API
* [todoapp](examples/todoapp) - TODO items tracker with persistence and multiple screens
+ * [todoapp-lite](examples/todoapp-lite) - A simplified version of [todoapp](examples/todoapp), fully based on Jetpack Compose
* [widgets gallery](examples/widgets-gallery) - Gallery of standard widgets
* [IDEA plugin](examples/intellij-plugin) - Plugin for IDEA using Compose for Desktop
* [gradle-plugins](gradle-plugins) - a plugin, simplifying usage of Compose Multiplatform with Gradle
diff --git a/examples/todoapp-lite/.gitignore b/examples/todoapp-lite/.gitignore
new file mode 100644
index 0000000000..a32b16597b
--- /dev/null
+++ b/examples/todoapp-lite/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build/
+/captures
+.externalNativeBuild
+.cxx
diff --git a/examples/todoapp-lite/README.md b/examples/todoapp-lite/README.md
new file mode 100755
index 0000000000..67023dabed
--- /dev/null
+++ b/examples/todoapp-lite/README.md
@@ -0,0 +1,20 @@
+A simplified version of the [TodoApp example](https://github.com/JetBrains/compose-jb/tree/master/examples/todoapp), fully based on Jetpack Compose and without using any third-party libraries.
+
+Supported targets: Android and Desktop.
+
+### Running desktop application
+```
+./gradlew :desktop:run
+```
+
+### Building native desktop distribution
+```
+./gradlew :desktop:package
+# outputs are written to desktop/build/compose/binaries
+```
+
+### Running Android application
+
+Open project in IntelliJ IDEA or Android Studio and run "android" configuration.
+
+![Desktop](screenshots/todoapplite.png)
diff --git a/examples/todoapp-lite/android/build.gradle.kts b/examples/todoapp-lite/android/build.gradle.kts
new file mode 100755
index 0000000000..4a07b39add
--- /dev/null
+++ b/examples/todoapp-lite/android/build.gradle.kts
@@ -0,0 +1,29 @@
+plugins {
+ id("com.android.application")
+ kotlin("android")
+ id("org.jetbrains.compose")
+}
+
+android {
+ compileSdk = 31
+
+ defaultConfig {
+ minSdk = 21
+ targetSdk = 31
+ versionCode = 1
+ versionName = "1.0"
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+ implementation(project(":common"))
+ implementation(compose.material)
+ implementation("androidx.appcompat:appcompat:1.3.0")
+ implementation("androidx.activity:activity-compose:1.3.0")
+}
+
diff --git a/examples/todoapp-lite/android/src/main/AndroidManifest.xml b/examples/todoapp-lite/android/src/main/AndroidManifest.xml
new file mode 100755
index 0000000000..e55a10c995
--- /dev/null
+++ b/examples/todoapp-lite/android/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/todoapp-lite/android/src/main/java/example/todoapp/lite/MainActivity.kt b/examples/todoapp-lite/android/src/main/java/example/todoapp/lite/MainActivity.kt
new file mode 100755
index 0000000000..0635d7d922
--- /dev/null
+++ b/examples/todoapp-lite/android/src/main/java/example/todoapp/lite/MainActivity.kt
@@ -0,0 +1,26 @@
+package example.todoapp.lite
+
+import android.os.Bundle
+import androidx.activity.compose.setContent
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.ui.Modifier
+import example.todoapp.lite.common.RootContent
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ MaterialTheme {
+ Surface(color = MaterialTheme.colors.background) {
+ RootContent(modifier = Modifier.fillMaxSize())
+ }
+ }
+ }
+ }
+}
+
diff --git a/examples/todoapp-lite/build.gradle.kts b/examples/todoapp-lite/build.gradle.kts
new file mode 100755
index 0000000000..72f7546fef
--- /dev/null
+++ b/examples/todoapp-lite/build.gradle.kts
@@ -0,0 +1,22 @@
+buildscript {
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+ }
+
+ dependencies {
+ // __LATEST_COMPOSE_RELEASE_VERSION__
+ classpath("org.jetbrains.compose:compose-gradle-plugin:1.0.0-alpha4-build331")
+ classpath("com.android.tools.build:gradle:4.1.0")
+ classpath(kotlin("gradle-plugin", version = "1.5.30"))
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+ }
+}
diff --git a/examples/todoapp-lite/common/build.gradle.kts b/examples/todoapp-lite/common/build.gradle.kts
new file mode 100755
index 0000000000..a80db145fd
--- /dev/null
+++ b/examples/todoapp-lite/common/build.gradle.kts
@@ -0,0 +1,44 @@
+import org.jetbrains.compose.compose
+
+plugins {
+ id("com.android.library")
+ kotlin("multiplatform")
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ android()
+ jvm("desktop")
+ sourceSets {
+ named("commonMain") {
+ dependencies {
+ implementation(compose.runtime)
+ implementation(compose.foundation)
+ implementation(compose.material)
+ implementation(compose.ui)
+ }
+ }
+ }
+}
+
+android {
+ compileSdk = 31
+
+ defaultConfig {
+ minSdk = 21
+ targetSdk = 31
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+ }
+
+ sourceSets {
+ named("main") {
+ manifest.srcFile("src/androidMain/AndroidManifest.xml")
+ res.srcDirs("src/androidMain/res")
+ }
+ }
+}
+
diff --git a/examples/todoapp-lite/common/src/androidMain/AndroidManifest.xml b/examples/todoapp-lite/common/src/androidMain/AndroidManifest.xml
new file mode 100755
index 0000000000..ccd0950b6f
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/examples/todoapp-lite/common/src/androidMain/kotlin/example/todoapp/lite/common/Utils.kt b/examples/todoapp-lite/common/src/androidMain/kotlin/example/todoapp/lite/common/Utils.kt
new file mode 100644
index 0000000000..b52bcab2c8
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/kotlin/example/todoapp/lite/common/Utils.kt
@@ -0,0 +1,75 @@
+@file:JvmName("Utils")
+
+package example.todoapp.lite.common
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.material.Button
+import androidx.compose.material.Card
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ProvideTextStyle
+import androidx.compose.material.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.dp
+
+internal actual val MARGIN_SCROLLBAR: Dp = 0.dp
+
+internal actual interface ScrollbarAdapter
+
+@Composable
+internal actual fun rememberScrollbarAdapter(scrollState: LazyListState): ScrollbarAdapter =
+ object : ScrollbarAdapter {}
+
+@Composable
+internal actual fun VerticalScrollbar(
+ modifier: Modifier,
+ adapter: ScrollbarAdapter
+) {
+ // no-op
+}
+
+@Composable
+internal actual fun Dialog(
+ title: String,
+ onCloseRequest: () -> Unit,
+ content: @Composable () -> Unit
+) {
+ androidx.compose.ui.window.Dialog(
+ onDismissRequest = onCloseRequest,
+ ) {
+ Card(elevation = 8.dp) {
+ Column(
+ modifier = Modifier
+ .padding(8.dp)
+ .height(IntrinsicSize.Min)
+ ) {
+ ProvideTextStyle(MaterialTheme.typography.subtitle1) {
+ Text(text = title)
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Box(modifier = Modifier.weight(1F)) {
+ content()
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Button(
+ onClick = onCloseRequest,
+ modifier = Modifier.align(Alignment.End)
+ ) {
+ Text(text = "Done")
+ }
+ }
+ }
+ }
+}
diff --git a/examples/todoapp-lite/common/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/examples/todoapp-lite/common/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000000..1f6bb29060
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/todoapp-lite/common/src/androidMain/res/drawable/ic_launcher_background.xml b/examples/todoapp-lite/common/src/androidMain/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000000..0d025f9bf6
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000000..6b78462d61
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000000..6b78462d61
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..898f3ed59a
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..dffca3601e
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..64ba76f75e
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..dae5e08234
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..e5ed46597e
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..14ed0af350
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..b0907cac3b
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..d8ae031549
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..2c18de9e66
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000000..beed3cdd2c
Binary files /dev/null and b/examples/todoapp-lite/common/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/examples/todoapp-lite/common/src/androidMain/res/values/strings.xml b/examples/todoapp-lite/common/src/androidMain/res/values/strings.xml
new file mode 100644
index 0000000000..5dee5cbdd0
--- /dev/null
+++ b/examples/todoapp-lite/common/src/androidMain/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ TodoApp Lite
+
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/EditDialog.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/EditDialog.kt
new file mode 100644
index 0000000000..308b83588c
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/EditDialog.kt
@@ -0,0 +1,51 @@
+package example.todoapp.lite.common
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.Checkbox
+import androidx.compose.material.Text
+import androidx.compose.material.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+
+@Composable
+internal fun EditDialog(
+ item: TodoItem,
+ onCloseClicked: () -> Unit,
+ onTextChanged: (String) -> Unit,
+ onDoneChanged: (Boolean) -> Unit,
+) {
+ Dialog(
+ title = "Edit todo",
+ onCloseRequest = onCloseClicked,
+ ) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally) {
+ TextField(
+ value = item.text,
+ modifier = Modifier.weight(1F).fillMaxWidth().sizeIn(minHeight = 192.dp),
+ label = { Text("Todo text") },
+ onValueChange = onTextChanged,
+ )
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Row {
+ Text(text = "Completed")
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ Checkbox(
+ checked = item.isDone,
+ onCheckedChange = onDoneChanged,
+ )
+ }
+ }
+ }
+}
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/MainContent.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/MainContent.kt
new file mode 100644
index 0000000000..7d8c390bdf
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/MainContent.kt
@@ -0,0 +1,158 @@
+package example.todoapp.lite.common
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.Checkbox
+import androidx.compose.material.Divider
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Text
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Delete
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+
+@Composable
+internal fun MainContent(
+ modifier: Modifier = Modifier,
+ items: List,
+ inputText: String,
+ onItemClicked: (id: Long) -> Unit,
+ onItemDoneChanged: (id: Long, isDone: Boolean) -> Unit,
+ onItemDeleteClicked: (id: Long) -> Unit,
+ onAddItemClicked: () -> Unit,
+ onInputTextChanged: (String) -> Unit,
+) {
+ Column(modifier) {
+ TopAppBar(title = { Text(text = "Todo List") })
+
+ Box(Modifier.weight(1F)) {
+ ListContent(
+ items = items,
+ onItemClicked = onItemClicked,
+ onItemDoneChanged = onItemDoneChanged,
+ onItemDeleteClicked = onItemDeleteClicked
+ )
+ }
+
+ Input(
+ text = inputText,
+ onAddClicked = onAddItemClicked,
+ onTextChanged = onInputTextChanged
+ )
+ }
+}
+
+@Composable
+private fun ListContent(
+ items: List,
+ onItemClicked: (id: Long) -> Unit,
+ onItemDoneChanged: (id: Long, isDone: Boolean) -> Unit,
+ onItemDeleteClicked: (id: Long) -> Unit,
+) {
+ Box {
+ val listState = rememberLazyListState()
+
+ LazyColumn(state = listState) {
+ items(items) { item ->
+ Item(
+ item = item,
+ onClicked = { onItemClicked(item.id) },
+ onDoneChanged = { onItemDoneChanged(item.id, it) },
+ onDeleteClicked = { onItemDeleteClicked(item.id) }
+ )
+
+ Divider()
+ }
+ }
+
+ VerticalScrollbar(
+ modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
+ adapter = rememberScrollbarAdapter(scrollState = listState)
+ )
+ }
+}
+
+@Composable
+private fun Item(
+ item: TodoItem,
+ onClicked: () -> Unit,
+ onDoneChanged: (Boolean) -> Unit,
+ onDeleteClicked: () -> Unit
+) {
+ Row(modifier = Modifier.clickable(onClick = onClicked)) {
+ Spacer(modifier = Modifier.width(8.dp))
+
+ Checkbox(
+ checked = item.isDone,
+ modifier = Modifier.align(Alignment.CenterVertically),
+ onCheckedChange = onDoneChanged,
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ Text(
+ text = AnnotatedString(item.text),
+ modifier = Modifier.weight(1F).align(Alignment.CenterVertically),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ IconButton(onClick = onDeleteClicked) {
+ Icon(
+ imageVector = Icons.Default.Delete,
+ contentDescription = null
+ )
+ }
+
+ Spacer(modifier = Modifier.width(MARGIN_SCROLLBAR))
+ }
+}
+
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+private fun Input(
+ text: String,
+ onTextChanged: (String) -> Unit,
+ onAddClicked: () -> Unit
+) {
+ Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) {
+ OutlinedTextField(
+ value = text,
+ modifier = Modifier
+ .weight(weight = 1F)
+ .onKeyUp(key = Key.Enter, action = onAddClicked),
+ onValueChange = onTextChanged,
+ label = { Text(text = "Add a todo") }
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ IconButton(onClick = onAddClicked) {
+ Icon(
+ imageVector = Icons.Default.Add,
+ contentDescription = null
+ )
+ }
+ }
+}
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootContent.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootContent.kt
new file mode 100644
index 0000000000..84fa449e36
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootContent.kt
@@ -0,0 +1,38 @@
+package example.todoapp.lite.common
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import example.todoapp.lite.common.RootStore.RootState
+
+@Composable
+fun RootContent(modifier: Modifier = Modifier) {
+ val model = remember { RootStore() }
+ val state = model.state
+
+ MainContent(
+ modifier = modifier,
+ items = state.items,
+ inputText = state.inputText,
+ onItemClicked = model::onItemClicked,
+ onItemDoneChanged = model::onItemDoneChanged,
+ onItemDeleteClicked = model::onItemDeleteClicked,
+ onAddItemClicked = model::onAddItemClicked,
+ onInputTextChanged = model::onInputTextChanged,
+ )
+
+ state.editingItem?.also { item ->
+ EditDialog(
+ item = item,
+ onCloseClicked = model::onEditorCloseClicked,
+ onTextChanged = model::onEditorTextChanged,
+ onDoneChanged = model::onEditorDoneChanged,
+ )
+ }
+}
+
+private val RootState.editingItem: TodoItem?
+ get() = editingItemId?.let(items::firstById)
+
+private fun List.firstById(id: Long): TodoItem =
+ first { it.id == id }
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootStore.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootStore.kt
new file mode 100644
index 0000000000..91dfe7df5b
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/RootStore.kt
@@ -0,0 +1,80 @@
+package example.todoapp.lite.common
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+
+internal class RootStore {
+
+ var state: RootState by mutableStateOf(initialState())
+ private set
+
+ fun onItemClicked(id: Long) {
+ setState { copy(editingItemId = id) }
+ }
+
+ fun onItemDoneChanged(id: Long, isDone: Boolean) {
+ setState {
+ updateItem(id = id) { it.copy(isDone = isDone) }
+ }
+ }
+
+ fun onItemDeleteClicked(id: Long) {
+ setState { copy(items = items.filterNot { it.id == id }) }
+ }
+
+ fun onAddItemClicked() {
+ setState {
+ val newItem =
+ TodoItem(
+ id = items.maxOfOrNull(TodoItem::id)?.plus(1L) ?: 1L,
+ text = inputText,
+ )
+
+ copy(items = items + newItem, inputText = "")
+ }
+ }
+
+ fun onInputTextChanged(text: String) {
+ setState { copy(inputText = text) }
+ }
+
+ fun onEditorCloseClicked() {
+ setState { copy(editingItemId = null) }
+ }
+
+ fun onEditorTextChanged(text: String) {
+ setState {
+ updateItem(id = requireNotNull(editingItemId)) { it.copy(text = text) }
+ }
+ }
+
+ fun onEditorDoneChanged(isDone: Boolean) {
+ setState {
+ updateItem(id = requireNotNull(editingItemId)) { it.copy(isDone = isDone) }
+ }
+ }
+
+ private fun RootState.updateItem(id: Long, transformer: (TodoItem) -> TodoItem): RootState =
+ copy(items = items.updateItem(id = id, transformer = transformer))
+
+ private fun List.updateItem(id: Long, transformer: (TodoItem) -> TodoItem): List =
+ map { item -> if (item.id == id) transformer(item) else item }
+
+ private fun initialState(): RootState =
+ RootState(
+ items = (1L..5L).map { id ->
+ TodoItem(id = id, text = "Some text $id")
+ }
+ )
+
+ private inline fun setState(update: RootState.() -> RootState) {
+ state = state.update()
+ }
+
+ data class RootState(
+ val items: List = emptyList(),
+ val inputText: String = "",
+ val editingItemId: Long? = null,
+ )
+}
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/TodoItem.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/TodoItem.kt
new file mode 100644
index 0000000000..94da3f2834
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/TodoItem.kt
@@ -0,0 +1,7 @@
+package example.todoapp.lite.common
+
+internal data class TodoItem(
+ val id: Long = 0L,
+ val text: String = "",
+ val isDone: Boolean = false
+)
diff --git a/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/Utils.kt b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/Utils.kt
new file mode 100644
index 0000000000..9345b740a2
--- /dev/null
+++ b/examples/todoapp-lite/common/src/commonMain/kotlin/example/todoapp/lite/common/Utils.kt
@@ -0,0 +1,41 @@
+package example.todoapp.lite.common
+
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.key.Key
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.type
+import androidx.compose.ui.unit.Dp
+
+internal expect val MARGIN_SCROLLBAR: Dp
+
+internal expect interface ScrollbarAdapter
+
+@Composable
+internal expect fun rememberScrollbarAdapter(scrollState: LazyListState): ScrollbarAdapter
+
+@Composable
+internal expect fun VerticalScrollbar(
+ modifier: Modifier,
+ adapter: ScrollbarAdapter
+)
+
+@Composable
+internal expect fun Dialog(
+ title: String,
+ onCloseRequest: () -> Unit,
+ content: @Composable () -> Unit
+)
+
+internal fun Modifier.onKeyUp(key: Key, action: () -> Unit): Modifier =
+ onKeyEvent { event ->
+ if ((event.type == KeyEventType.KeyUp) && (event.key == key)) {
+ action()
+ true
+ } else {
+ false
+ }
+ }
diff --git a/examples/todoapp-lite/common/src/desktopMain/kotlin/example/todoapp/lite/common/Utils.kt b/examples/todoapp-lite/common/src/desktopMain/kotlin/example/todoapp/lite/common/Utils.kt
new file mode 100644
index 0000000000..1e36024aa1
--- /dev/null
+++ b/examples/todoapp-lite/common/src/desktopMain/kotlin/example/todoapp/lite/common/Utils.kt
@@ -0,0 +1,55 @@
+@file:JvmName("Utils")
+
+package example.todoapp.lite.common
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyListState
+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.dp
+
+internal actual val MARGIN_SCROLLBAR: Dp = 8.dp
+
+@Suppress("ACTUAL_WITHOUT_EXPECT") // Workaround https://youtrack.jetbrains.com/issue/KT-37316
+internal actual typealias ScrollbarAdapter = androidx.compose.foundation.ScrollbarAdapter
+
+@Composable
+internal actual fun rememberScrollbarAdapter(scrollState: LazyListState): ScrollbarAdapter =
+ androidx.compose.foundation.rememberScrollbarAdapter(scrollState)
+
+@Composable
+internal actual fun VerticalScrollbar(
+ modifier: Modifier,
+ adapter: ScrollbarAdapter
+) {
+ androidx.compose.foundation.VerticalScrollbar(
+ modifier = modifier,
+ adapter = adapter
+ )
+}
+
+@Composable
+internal actual fun Dialog(
+ title: String,
+ onCloseRequest: () -> Unit,
+ content: @Composable () -> Unit
+) {
+ androidx.compose.ui.window.Dialog(
+ onCloseRequest = onCloseRequest,
+ focusable = true,
+ title = title,
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(8.dp),
+ contentAlignment = Alignment.Center,
+ ) {
+ content()
+ }
+ }
+}
diff --git a/examples/todoapp-lite/desktop/build.gradle.kts b/examples/todoapp-lite/desktop/build.gradle.kts
new file mode 100755
index 0000000000..e634f8ca9c
--- /dev/null
+++ b/examples/todoapp-lite/desktop/build.gradle.kts
@@ -0,0 +1,40 @@
+import org.jetbrains.compose.compose
+import org.jetbrains.compose.desktop.application.dsl.TargetFormat
+
+plugins {
+ kotlin("multiplatform") // kotlin("jvm") doesn't work well in IDEA/AndroidStudio (https://github.com/JetBrains/compose-jb/issues/22)
+ id("org.jetbrains.compose")
+}
+
+kotlin {
+ jvm {
+ withJava()
+ }
+ sourceSets {
+ named("jvmMain") {
+ dependencies {
+ implementation(compose.desktop.currentOs)
+ implementation(project(":common"))
+ }
+ }
+ }
+}
+
+compose.desktop {
+ application {
+ mainClass = "example.todoapp.lite.MainKt"
+
+ nativeDistributions {
+ targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
+ packageName = "TodoApp Lite"
+ packageVersion = "1.0.0"
+
+ windows {
+ menuGroup = "Compose Examples"
+ // see https://wixtoolset.org/documentation/manual/v3/howtos/general/generate_guids.html
+ upgradeUuid = "5ac63736-d8c7-4a65-a66b-6870df88ddfe"
+ }
+ }
+ }
+}
+
diff --git a/examples/todoapp-lite/desktop/src/jvmMain/kotlin/example/todoapp/lite/Main.kt b/examples/todoapp-lite/desktop/src/jvmMain/kotlin/example/todoapp/lite/Main.kt
new file mode 100644
index 0000000000..835c00b79b
--- /dev/null
+++ b/examples/todoapp-lite/desktop/src/jvmMain/kotlin/example/todoapp/lite/Main.kt
@@ -0,0 +1,29 @@
+package example.todoapp.lite
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material.MaterialTheme
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.WindowPosition
+import androidx.compose.ui.window.application
+import androidx.compose.ui.window.rememberWindowState
+import example.todoapp.lite.common.RootContent
+
+fun main() {
+ application {
+ Window(
+ onCloseRequest = ::exitApplication,
+ title = "TodoApp Lite",
+ state = rememberWindowState(
+ position = WindowPosition(alignment = Alignment.Center),
+ ),
+ ) {
+ MaterialTheme {
+ RootContent(
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
+ }
+}
diff --git a/examples/todoapp-lite/gradle.properties b/examples/todoapp-lite/gradle.properties
new file mode 100755
index 0000000000..4d15d015f8
--- /dev/null
+++ b/examples/todoapp-lite/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
\ No newline at end of file
diff --git a/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.jar b/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000..f3d88b1c2f
Binary files /dev/null and b/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.properties b/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000000..05679dc3c1
--- /dev/null
+++ b/examples/todoapp-lite/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/examples/todoapp-lite/gradlew b/examples/todoapp-lite/gradlew
new file mode 100755
index 0000000000..fbd7c51583
--- /dev/null
+++ b/examples/todoapp-lite/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/examples/todoapp-lite/gradlew.bat b/examples/todoapp-lite/gradlew.bat
new file mode 100755
index 0000000000..5093609d51
--- /dev/null
+++ b/examples/todoapp-lite/gradlew.bat
@@ -0,0 +1,104 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/examples/todoapp-lite/screenshots/todoapplite.png b/examples/todoapp-lite/screenshots/todoapplite.png
new file mode 100644
index 0000000000..39702b6b87
Binary files /dev/null and b/examples/todoapp-lite/screenshots/todoapplite.png differ
diff --git a/examples/todoapp-lite/settings.gradle.kts b/examples/todoapp-lite/settings.gradle.kts
new file mode 100755
index 0000000000..9a0d554e9b
--- /dev/null
+++ b/examples/todoapp-lite/settings.gradle.kts
@@ -0,0 +1 @@
+include(":common", ":android", ":desktop")