Making a Multipart API Call in Ktor with Jetpack Compose: A Simple Guide

Mukesh RajputMukesh Rajput
3 min read

In Android development, you might encounter scenarios where you need to send images or other files to a server. This can seem tricky, but with Ktor and Jetpack Compose, it’s simpler than you think. In this post, we’ll walk through how to make a multipart API call in Ktor to upload an image using Jetpack Compose.

Why Ktor and Jetpack Compose?

Ktor is a powerful framework for building connected applications. It makes handling HTTP requests and responses straightforward. Jetpack Compose is Android’s modern toolkit for building native UI. Combining these two allows you to create smooth, responsive, and visually appealing apps while handling complex backend interactions effortlessly.

Setting Up Ktor

First, you need to add the necessary dependencies to your build.gradle file:

implementation("io.ktor:ktor-client-core:2.x.x")
implementation("io.ktor:ktor-client-cio:2.x.x")
implementation("io.ktor:ktor-client-content-negotiation:2.x.x")
implementation("io.ktor:ktor-client-json:2.x.x")
implementation("io.ktor:ktor-client-serialization:2.x.x")

Replace 2.x.x with the latest version available.

Creating the Multipart Request

Let’s create a simple function to send an image to a server.

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.engine.cio.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.*
import java.io.File

suspend fun uploadImage(imageFile: File): HttpResponse {
    val client = HttpClient(CIO) {
        install(ContentNegotiation) {
            json()
        }
    }

    return client.use {
        it.submitFormWithBinaryData(
            url = "https://yourapi.com/upload",
            formData = formData {
                append(
                    "image",
                    imageFile.readBytes(),
                    Headers.build {
                        append(HttpHeaders.ContentType, ContentType.Image.Any)
                        append(HttpHeaders.ContentDisposition, "filename=${imageFile.name}")
                    }
                )
            }
        )
    }
}

Explanation:

  • HttpClient(CIO): Creates a new HTTP client with the CIO engine.

  • install(ContentNegotiation): Configures the client to use JSON for content negotiation.

  • submitFormWithBinaryData: Sends a form with binary data (in this case, an image).

  • formData: Constructs the form data with the image file.

Integrating with Jetpack Compose

Now, let’s integrate this function with a simple UI built using Jetpack Compose. We’ll create a button that, when clicked, will trigger the image upload.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import java.io.File

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            UploadImageScreen()
        }
    }
}

@Composable
fun UploadImageScreen() {
    var isUploading by remember { mutableStateOf(false) }
    val context = LocalContext.current

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center,
        modifier = Modifier.fillMaxSize().padding(16.dp)
    ) {
        Button(
            onClick = {
                isUploading = true
                // Replace with your actual image file path
                val imageFile = File(context.cacheDir, "example.png")

                CoroutineScope(Dispatchers.IO).launch {
                    val response = uploadImage(imageFile)
                    isUploading = false
                    // Handle the response (e.g., show a success message)
                }
            },
            enabled = !isUploading
        ) {
            Text(if (isUploading) "Uploading..." else "Upload Image")
        }
    }
}

Conclusion

That’s it! With these simple steps, you can now upload images from your Android app using Ktor and Jetpack Compose. This combination provides a robust and user-friendly way to handle complex tasks, all while keeping your code clean and easy to understand.

If you enjoyed this tutorial, feel free to share it and stay tuned for more Android development tips and tricks!

10
Subscribe to my newsletter

Read articles from Mukesh Rajput directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mukesh Rajput
Mukesh Rajput

Specializing in creating scalable and maintainable applications using MVVM and Clean Architecture principles. With expertise in Ktor, Retrofit, RxJava, View Binding, Data Binding, Hilt, Koin, Coroutines, Room, Realm, and Firebase, I am committed to delivering high-quality mobile solutions that provide seamless user experiences. I thrive on new challenges and am constantly seeking opportunities to innovate in the Android development space.