---
title: Visual Preview in Android
description: Enhance your editorial experience by previewing draft content in an Android app.
url: https://www.storyblok.com/docs/guides/android/visual-preview
---

# Visual Preview in Android

Enhance your editorial and development experience by previewing draft content in the debug build of your Android app.

## Set version based on build type

First, add the following to `app/build.gradle.kts` to enable `BuildConfig`.

app/build.gradle.kts

```kotlin
plugins {
  alias(libs.plugins.android.application)
  alias(libs.plugins.kotlin.compose)
  alias(libs.plugins.kotlin.serialization)
}

android {
  …
  buildFeatures {
    compose = true
    buildConfig = true
  }
}
…
```

Then replace the following in `MainActivity.kt` to set `version` based on the build type.

app/src/main/java/com/example/myapplication/MainActivity.kt

```kotlin
…
class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContent {
      MyApplicationTheme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
          Storyblok(
            accessToken = "YOUR_ACCESS_TOKEN",
            version = Draft,
            version = if (BuildConfig.DEBUG) Draft else Published,
            region = EU, // Choose the correct region from your Space.
            …
          ) {
            val story by story("home").collectAsStateWithLifecycle(null)
            story?.content?.let { Block(it, Modifier.padding(innerPadding)) }
          }
        }
      }
    }
  }
}
…
```

With these changes, debug builds of the Android app now fetch draft content from Storyblok, while release builds fetch published content.

## Refresh draft content

With `version` set to `Draft`, any changes you save in Storyblok appear the next time the [`story()`](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose/-storyblok-scope/index.html#816830072%2FFunctions%2F-825163495) function is invoked.

Replace the following in `MainActivity.kt` with a pull-to-refresh implementation so the story can be refreshed without restarting the app.

app/src/main/java/com/example/myapplication/MainActivity.kt

```kotlin
…
import androidx.compose.ui.unit.dp
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.onCompletion

class MainActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    enableEdgeToEdge()
    setContent {
      MyApplicationTheme {
        Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
          Storyblok(
            accessToken = "YOUR_ACCESS_TOKEN",
            version = if (BuildConfig.DEBUG) Draft else Published,
            region = EU, // Choose the correct region from your Space.
            …
          ) {
            val story by story("home").collectAsStateWithLifecycle(null)
            story?.content?.let { Block(it, Modifier.padding(innerPadding)) }
            var isRefreshing: Boolean by remember { mutableStateOf(true) }

            val story by
              remember {
                snapshotFlow { isRefreshing }
                  .filter { it }
                  .flatMapLatest {
                    story("home").onCompletion { isRefreshing = false }
                  }
              }
              .collectAsStateWithLifecycle(null)

            PullToRefreshBox(
              isRefreshing = isRefreshing,
              onRefresh = { isRefreshing = true },
              modifier = Modifier.fillMaxSize()
            ) {
              story?.content?.let { Block(it, Modifier.padding(innerPadding)) }
            }
          }
        }
      }
    }
  }
}
…
```

The [`story()`](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose/-storyblok-scope/index.html#816830072%2FFunctions%2F-825163495) function is now invoked as follows:

-   The new `isRefreshing` state starts as `true` to trigger the initial load automatically.
-   `snapshotFlow { isRefreshing }` converts the state to a `Flow`, and `.filter { it }` ensures the story is only fetched when `isRefreshing` becomes `true`.
-   `.flatMapLatest { ... }` cancels any in-flight story request and invokes [`story()`](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/com.storyblok.compose/-storyblok-scope/index.html#816830072%2FFunctions%2F-825163495) to start a new one, setting `isRefreshing` back to `false` when the flow completes.
-   `PullToRefreshBox` wraps the story content and triggers a refresh when the user pulls down.

With this in place, make a change to the `home` story, save it, then pull down in the debug build to immediately see the updated draft content.

## Related resources

[Storyblok Kotlin Compose SDK Reference](https://storyblok.github.io/storyblok-kotlin/storyblok-compose/index.html)

[Content Delivery API: Retrieve a Single Story](/docs/api/content-delivery/v2/stories/retrieve-a-single-story)

[Android Developers: Pull to Refresh in Jetpack Compose](https://developer.android.com/develop/ui/compose/components/pull-to-refresh)

## Pagination

-   [Previous: Integrate Android with Storyblok](/docs/guides/android)
-   [Next: Dynamic Navigation in Android](/docs/guides/android/dynamic-routing)
