Skip to content

update: adding a Wasm target to the full stack KMP tutorial #631

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ kotlin {
iosMain.dependencies {
implementation(libs.ktor.client.darwin)
}
wasmJsMain.dependencies {
implementation(libs.ktor.client.wasm)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.example.ktor.full_stack_task_manager

import Priority
import Task
import TaskApi
import com.example.ktor.full_stack_task_manager.model.Priority
import com.example.ktor.full_stack_task_manager.model.Task
import com.example.ktor.full_stack_task_manager.network.TaskApi
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.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
Expand All @@ -18,15 +19,11 @@ import androidx.compose.material3.Card
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.material3.TextFieldDefaults
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import com.example.ktor.full_stack_task_manager.network.createHttpClient
import kotlinx.coroutines.launch

Expand All @@ -37,26 +34,16 @@ fun App() {
val taskApi = remember { TaskApi(httpClient) }
var tasks by remember { mutableStateOf(emptyList<Task>()) }
val scope = rememberCoroutineScope()
var currentTask by remember { mutableStateOf<Task?>(null) }

LaunchedEffect(Unit) {
tasks = taskApi.getAllTasks()
}

if (currentTask != null) {
UpdateTaskDialog(
currentTask!!,
onConfirm = {
scope.launch {
taskApi.updateTask(it)
tasks = taskApi.getAllTasks()
}
currentTask = null
}
)
}

LazyColumn(modifier = Modifier.fillMaxSize().padding(8.dp)) {
LazyColumn(
modifier = Modifier
.safeContentPadding()
.fillMaxSize()
) {
items(tasks) { task ->
TaskCard(
task,
Expand All @@ -67,7 +54,6 @@ fun App() {
}
},
onUpdate = {
currentTask = task
}
)
}
Expand Down Expand Up @@ -109,54 +95,4 @@ fun TaskCard(
}
}
}
}

@Composable
fun UpdateTaskDialog(
task: Task,
onConfirm: (Task) -> Unit
) {
var description by remember { mutableStateOf(task.description) }
var priorityText by remember { mutableStateOf(task.priority.toString()) }
val colors = TextFieldDefaults.colors(
focusedTextColor = Color.Blue,
focusedContainerColor = Color.White,
)

Dialog(onDismissRequest = {}) {
Card(
modifier = Modifier.fillMaxWidth().padding(4.dp),
shape = RoundedCornerShape(CornerSize(4.dp))
) {
Column(modifier = Modifier.padding(10.dp)) {
Text("Update ${task.name}", fontSize = 20.sp)
TextField(
value = description,
onValueChange = { description = it },
label = { Text("Description") },
colors = colors
)
TextField(
value = priorityText,
onValueChange = { priorityText = it },
label = { Text("Priority") },
colors = colors
)
OutlinedButton(onClick = {
val newTask = Task(
task.name,
description,
try {
Priority.valueOf(priorityText)
} catch (e: IllegalArgumentException) {
Priority.Low
}
)
onConfirm(newTask)
}) {
Text("Update")
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.example.ktor.full_stack_task_manager

import com.example.ktor.full_stack_task_manager.model.Task
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.example.ktor.full_stack_task_manager.network.TaskApi
import com.example.ktor.full_stack_task_manager.network.createHttpClient
import kotlinx.coroutines.launch

@Composable
fun App() {
MaterialTheme {
val httpClient = createHttpClient()
val taskApi = remember { TaskApi(httpClient) }
val tasks = remember { mutableStateOf(emptyList<Task>()) }
val scope = rememberCoroutineScope()

Column(
modifier = Modifier
.safeContentPadding()
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Button(onClick = {
scope.launch {
tasks.value = taskApi.getAllTasks()
}
}) {
Text("Fetch Tasks")
}
for (task in tasks.value) {
Text(task.name)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import com.example.ktor.full_stack_task_manager.model.Task
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.delete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
ktor-client-wasm = { module = "io.ktor:ktor-client-js-wasm-js", version.ref = "ktor"}
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
ktor-serialization-kotlinx-json-jvm = { module = "io.ktor:ktor-serialization-kotlinx-json-jvm", version.ref = "ktor" }
ktor-server-content-negotiation-jvm = { module = "io.ktor:ktor-server-content-negotiation-jvm", version.ref = "ktor" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
package com.example.ktor.full_stack_task_manager

import InMemoryTaskRepository
import Task
import io.ktor.http.HttpStatusCode
import io.ktor.serialization.JsonConvertException
import io.ktor.serialization.kotlinx.json.json
import com.example.ktor.full_stack_task_manager.model.InMemoryTaskRepository
import com.example.ktor.full_stack_task_manager.model.Priority
import com.example.ktor.full_stack_task_manager.model.Task
import io.ktor.http.*
import io.ktor.serialization.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.request.receive
import io.ktor.server.plugins.contentnegotiation.*
import io.ktor.server.plugins.cors.routing.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

Expand All @@ -22,7 +24,12 @@ fun Application.module() {
install(ContentNegotiation) {
json()
}

install(CORS) {
allowHeader(HttpHeaders.ContentType)
// For ease of demonstration we allow any connections.
// Don't do this in production if possible.
anyHost()
}
val repository = InMemoryTaskRepository()

routing {
Expand Down
Binary file modified images/full_stack_development_tutorial_run_android.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading