# Flagbearer for Android

The Kotlin SDK for Flagbearer, for Android apps. Requires Android 6.0 (API 23)
or later and has no dependencies.

Server-side Kotlin uses the [Java SDK](/sdks/java) instead.

## Install

Gradle (Kotlin DSL):

```kotlin
dependencies {
    implementation("dev.flagbearer:flagbearer-android:VERSION")
}
```

Replace `VERSION` with the latest release, listed on
[Maven Central](https://central.sonatype.com/artifact/dev.flagbearer/flagbearer-android).

## Keys

Use a client key, which starts with `fbc_`. Client keys are public and safe to
include in an app. Server keys, which start with `fbs_`, are secret and belong
only on your server.

## Set up one client

Create one client for the whole app, in your `Application` class:

```kotlin
import dev.flagbearer.android.Flagbearer

class App : Application() {
    lateinit var flags: Flagbearer

    override fun onCreate() {
        super.onCreate()
        flags = Flagbearer(this, clientKey = "fbc_...")
    }
}
```

Flags load in the background and are kept on the device, so the app starts with
the flags each user last had, even offline.

While the app is in the foreground, a change you make in the dashboard reaches
it within a couple of seconds, over a live connection. If that connection
drops, the SDK checks every 30 seconds until it's back. After five minutes in
the background it stops both, and catches up when the app returns.

## Users

When the signed-in user is known or changes, call `identify` from a coroutine:

```kotlin
flags.identify(User(key = user.id, email = user.email))
```

The email is optional. Include it to turn flags on for testers by email. After
sign-out, call `identify(User())`.

## Checking a flag

```kotlin
if (flags.isEnabled("new-checkout")) {
    showNewCheckout()
}
```

`isEnabled` never waits and never throws. It returns `false` until the current
user's flags have loaded, and when the flag doesn't exist. Pass another default
as `flags.isEnabled("new-checkout", defaultValue = true)`.

`flags.status` is `FlagsStatus.LOADING` until the current user's flags arrive.
Use it to hold back UI that mustn't switch between the default and the real
value.

## Jetpack Compose

Read a flag as state, so the screen redraws when it changes:

```kotlin
@Composable
fun rememberFlag(flags: Flagbearer, key: String, defaultValue: Boolean = false): Boolean {
    var enabled by remember(key) { mutableStateOf(flags.isEnabled(key, defaultValue)) }
    DisposableEffect(key) {
        val subscription = flags.subscribe { enabled = flags.isEnabled(key, defaultValue) }
        onDispose { subscription.close() }
    }
    return enabled
}
```

```kotlin
if (rememberFlag(flags, "new-checkout")) NewCheckout() else Checkout()
```

`subscribe` calls its listener on the main thread whenever flags or `status`
change. Close what it returns to stop listening.

## Error reports

`allFlags()` returns every flag's current value, so error reports show exactly
which flags were on. It doesn't count as checking them.

```kotlin
crashReporter.setContext("flags", flags.allFlags())
// {new-checkout=true, dark-mode=false}
```

## When something is wrong

The SDK never crashes or blocks the app over a flag problem. It keeps the last
flags, or defaults, and says what's wrong in Logcat under the `Flagbearer` tag:

- A key the server refuses is explained once, with the server's reason.
- A server key (`fbs_`) is never sent. The log says to use a client key.
- In debuggable builds, checking a flag that doesn't exist warns once per flag.

Pass `debug = true` to also log each fetch, and to get the missing-flag warning
in release builds.

## Options

```kotlin
val flags = Flagbearer(
    context = this,
    clientKey = "fbc_...",
    user = User(key = "42"), // the user at launch, if already known
    debug = false,
    appVersion = null,       // defaults to the app's versionName
    cacheFlags = true,       // start with the flags this user last had
)
```
