Getting Started

Framework Quickstarts

Use Supabase with Android Kotlin

Learn how to create a Supabase project, add some sample data to your database, and query the data from an Android Kotlin app.

1

Set up a Supabase project

Create a new project in the Supabase Dashboard.

After your project is ready, create a table in your Supabase database using the SQL Editor in the Dashboard. Use the following SQL statement to create all tables.

SQL_EDITOR
-- Create products table
create table
public.products (
_id bigint generated by default as identity not null,
productid text not null,
name text null,
description text null,
price real null,
image text null,
category text null,
nutrition text null,
constraint products_pkey primary key (productid),
) tablespace pg_default;
2

Create an Android app with Android Studio

Open Android Studio > New > New Android Project.

3

Install the Supabase client library

Import Supabase and all required dependencies. Replace the version placeholders $supabase_version and $ktor_version with the respective latest versions.

implementation "io.github.jan-tennert.supabase:postgrest-kt:$supabase_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
4

Install the serializable plugin

Open the build.gradle (app), add the serialization plugin to use annotation for data parsing. Please note that the plugin version should be the same as the Kotlin version in your app.

plugins {
...
id 'org.jetbrains.kotlin.plugin.serialization' version '$kotlin_version'
...
}
5

Initialize the Supabase client

You can create a Supabase client whenever you need to perform an API call. That being said, it is recommended to use a dependency injection library like Hilt.

val client = createSupabaseClient(
supabaseUrl = "https://xyzcompany.supabase.co",
supabaseKey = "public-anon-key"
) {
install(Postgrest)
}
6

Create a data transfer object

@Serializable
data class ProductDto(
@SerialName("productid")
val productId: String,
@SerialName("name")
val name: String,
@SerialName("description")
val description: String,
@SerialName("price")
val price: Double,
@SerialName("image")
val image: String,
@SerialName("category")
val category: String,
@SerialName("nutrition")
val nutrition: String,
@SerialName("_id")
val _id: Int,
)
7

Create a domain object

This kind of object will be consumed by the view.

data class Product(
val productId: String,
val name: String,
val description: String,
val price: Double,
val image: String,
val category: String,
val nutrition: String,
val _id: Int,
)
8

Query data from the app

Create a repository to interact with the data source.

interface ProductRepository {
fun getProducts(): List<ProductDto>
}

class ProductRepositoryImpl @Inject constructor(
private val postgrest: Postgrest,
) : ProductRepository {
override suspend fun getProducts(): List<ProductDto> {
val result = client.postgrest["products"]
.select().decodeList<ProductDto>()
// Handle result data for next step
return result
}
}
9

Create a module to provide repository

Use Hilt for dependency injection.

InstallIn(SingletonComponent::class)
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository
}
10

Get data from ViewModel inside a coroutine scope

Add the @Inject annotation to use the repository in a ViewModel.

class ProductListViewModel @Inject constructor(
private val productRepository: ProductRepository
) : ViewModel() {

private val _productList = MutableStateFlow<List<Product>?>(listOf())
val productList: Flow<List<Product>?> = _productList

init {
getProducts()
}

fun getProducts() {
viewModelScope.launch {
val products = productRepository.getProducts()
_productList.emit(products?.map { it -> it.asDomainModel() })
}
}

private fun ProductDto.asDomainModel(): Product {
return Product(
productId = this.productId,
name = this.name,
price = this.price,
image = this.image,
description = this.description,
category = this.category,
nutrition = this.nutrition,
_id = this._id
)
}
11

Observe data in a Composable

@Composable
fun ProductListScreen(
modifier: Modifier = Modifier,
navController: NavController,
viewModel: ProductListViewModel = hiltViewModel(),
) {
val productList = viewModel.productList.collectAsState(initial = listOf()).value
if (!productList.isNullOrEmpty()) {
LazyColumn(
modifier = modifier.padding(24.dp),
contentPadding = PaddingValues(5.dp)
) {
items(productList) { item ->
ProductListItem(
product = item,
modifier = modifier,
onClick = {
navController.navigate(
ProductDetailsDestination.createRouteWithParam(
item.id
)
)
},
)
}
}
}
}
12

Start the app


We only collect analytics essential to ensuring smooth operation of our services.

Learn more