> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Kotlin Quickstart

> Install the Dynamic Kotlin SDK on Android with AARs, initialize it in Compose, and sign users in with the built-in UI.

<Tip>
  **📱 Complete Example App**: For a fully functional Android app demonstrating all SDK
  features, check out our [Android SDK Example App](https://github.com/dynamic-labs/android-sdk-and-sample-app/tree/main/ExampleApp/DynamicSDKExample). It
  includes authentication flows (email/SMS OTP, social login, passkeys), wallet management, EVM/Solana transactions,
  MFA, and passkey examples with Jetpack Compose.
</Tip>

<Accordion title="Prerequisites">
  Before you begin, make sure you have:

  * **Android SDK 28+** (Android 9.0+)
  * **Kotlin 2.1.0+**
  * **Gradle 8.x+**
  * **Java 17**
  * **Dynamic Account** — Environment ID from [Dynamic Dashboard](https://app.dynamic.xyz/dashboard/overview)
</Accordion>

## Install the SDK

This guide uses AAR-based setup (there is no interactive embed on this page).

### Step 1: Download AAR files

Download the latest AAR files from the [Dynamic Android SDK repository](https://github.com/dynamic-labs/android-sdk-and-sample-app):

* `dynamic-sdk-android.aar` (required)
* `solana-web3.aar` (optional - only if you need Solana support)

Place them in your project's `app/libs` directory.

### Step 2: Add dependencies

Add the Dynamic SDK and all required dependencies to your `build.gradle.kts`:

```kotlin build.gradle.kts theme={"system"}
dependencies {
    // ==========================================
    // Dynamic SDK AAR Files
    // ==========================================

    // Core SDK (required)
    implementation(files("libs/dynamic-sdk-android.aar"))

    // Solana SDK (optional - remove if not needed)
    implementation(files("libs/solana-web3.aar"))
    implementation("org.sol4k:sol4k:0.6.0")  // Required by Dynamic Solana SDK

    // ==========================================
    // Required transitive dependencies
    // ==========================================

    // Kotlin Coroutines
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0")

    // JSON serialization
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")

    // Android WebView
    implementation("androidx.webkit:webkit:1.8.0")

    // HTTP client
    implementation("com.squareup.okhttp3:okhttp:4.12.0")

    // Custom Tabs for authentication
    implementation("androidx.browser:browser:1.7.0")

    // Secure storage - DataStore + Tink
    implementation("androidx.datastore:datastore-preferences:1.1.1")
    implementation("com.google.crypto.tink:tink-android:1.15.0")

    // Passkeys support
    implementation("androidx.credentials:credentials:1.2.2")
    implementation("androidx.credentials:credentials-play-services-auth:1.2.2")
    implementation("com.google.android.gms:play-services-auth:20.7.0")

    // Jetpack Compose (required for DynamicUI)
    implementation(platform("androidx.compose:compose-bom:2024.09.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.foundation:foundation")
    implementation("androidx.compose.runtime:runtime")
    implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}
```

<Note>
  If you don't need Solana support, you can skip the `solana-web3.aar` and `sol4k` dependencies.
</Note>

## Configure AndroidManifest.xml

Add the required permissions and activities to your `AndroidManifest.xml`:

```xml AndroidManifest.xml theme={"system"}
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:usesCleartextTraffic="true"
        ...>

        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- OAuth Callback Activity (Required) -->
        <activity
            android:name="com.dynamic.sdk.android.Auth.AuthCallbackActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:noHistory="true"
            android:theme="@android:style/Theme.Translucent.NoTitleBar">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="yourappscheme" />
            </intent-filter>
        </activity>
    </application>
</manifest>
```

<Warning>
  Make sure the `android:scheme` in your manifest matches the `redirectUrl` in your `ClientProps`. For example, if `redirectUrl = "myapp://"`, then use `android:scheme="myapp"`.
</Warning>

## Initialize the SDK

Initialize the Dynamic SDK in your MainActivity:

```kotlin MainActivity.kt theme={"system"}
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.ui.Modifier
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.UI.DynamicUI
import com.dynamic.sdk.android.core.ClientProps
import com.dynamic.sdk.android.core.LoggerLevel

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

        // Initialize Dynamic SDK
        val props = ClientProps(
            environmentId = "your-environment-id",
            appLogoUrl = "https://your-app.com/logo.png",
            appName = "Your App Name",
            redirectUrl = "yourappscheme://",
            appOrigin = "https://your-app.com",
            logLevel = LoggerLevel.DEBUG
        )
        DynamicSDK.initialize(props, applicationContext, this)

        setContent {
            MaterialTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Box(modifier = Modifier.fillMaxSize()) {
                        // Main app content
                        AppContent()

                        // Dynamic SDK WebView overlay (required for auth flows)
                        DynamicUI()
                    }
                }
            }
        }
    }
}
```

### Configuration Options

The `ClientProps` supports various configuration options:

| Property         | Description                                                                                              | Required |
| ---------------- | -------------------------------------------------------------------------------------------------------- | -------- |
| `environmentId`  | Your Dynamic environment ID from the dashboard                                                           | Yes      |
| `appName`        | Your app's display name                                                                                  | Yes      |
| `appLogoUrl`     | URL to your app's logo (shown in auth UI)                                                                | Yes      |
| `redirectUrl`    | Deep link URL scheme for OAuth callbacks                                                                 | Yes      |
| `appOrigin`      | Your app's origin URL                                                                                    | Yes      |
| `reownProjectId` | Reown (WalletConnect) project ID for [connecting to dApps](/kotlin/wallets/global-wallets/walletconnect) | No       |
| `logLevel`       | Logging level (`.DEBUG`, `.INFO`, `.WARN`, `.ERROR`)                                                     | No       |

### Access the SDK

After initialization, access the SDK singleton anywhere in your app:

```kotlin theme={"system"}
val sdk = DynamicSDK.getInstance()

// Show auth modal
sdk.ui.showAuth()

// Check auth state
val isAuthenticated = sdk.auth.isAuthenticated()

// Get wallets
val wallets = sdk.wallets.userWallets

// Get primary wallet
val primaryWallet = sdk.wallets.primaryWallet
```

## Basic Usage

### Using Built-in Authentication UI

The easiest way to add authentication is using the built-in UI:

```kotlin theme={"system"}
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.dynamic.sdk.android.DynamicSDK

@Composable
fun LoginScreen() {
    val sdk = DynamicSDK.getInstance()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        Button(
            onClick = { sdk.ui.showAuth() },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Sign In")
        }
    }
}
```

### Listening for Authentication State

Use Kotlin Flow to react to authentication changes:

```kotlin theme={"system"}
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.UserProfile
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class AuthViewModel : ViewModel() {
    private val sdk = DynamicSDK.getInstance()

    private val _user = MutableStateFlow<UserProfile?>(null)
    val user: StateFlow<UserProfile?> = _user

    private val _isAuthenticated = MutableStateFlow(false)
    val isAuthenticated: StateFlow<Boolean> = _isAuthenticated

    init {
        // Check initial auth state
        _isAuthenticated.value = sdk.auth.isAuthenticated()
        _user.value = sdk.auth.authenticatedUser

        // Listen for auth state changes
        viewModelScope.launch {
            sdk.auth.authenticatedUserChanges.collect { user ->
                _user.value = user
                _isAuthenticated.value = user != null
            }
        }
    }
}

@Composable
fun AppContent(viewModel: AuthViewModel) {
    val isAuthenticated by viewModel.isAuthenticated.collectAsState()

    if (isAuthenticated) {
        HomeScreen()
    } else {
        LoginScreen()
    }
}
```

### Complete Example with Wallets

```kotlin theme={"system"}
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dynamic.sdk.android.DynamicSDK
import com.dynamic.sdk.android.Models.BaseWallet
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class HomeViewModel : ViewModel() {
    private val sdk = DynamicSDK.getInstance()

    private val _wallets = MutableStateFlow<List<BaseWallet>>(emptyList())
    val wallets: StateFlow<List<BaseWallet>> = _wallets

    init {
        // Get current wallets
        _wallets.value = sdk.wallets.userWallets

        // Listen for wallet changes
        viewModelScope.launch {
            sdk.wallets.userWalletsChanges.collect { newWallets ->
                _wallets.value = newWallets
            }
        }
    }

    fun showUserProfile() {
        sdk.ui.showUserProfile()
    }

    fun logout() {
        viewModelScope.launch {
            sdk.auth.logout()
        }
    }
}

@Composable
fun HomeScreen(viewModel: HomeViewModel) {
    val wallets by viewModel.wallets.collectAsState()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        Text(
            text = "Welcome!",
            style = MaterialTheme.typography.headlineMedium
        )

        Spacer(modifier = Modifier.height(16.dp))

        if (wallets.isEmpty()) {
            CircularProgressIndicator()
            Text("Creating wallets...")
        } else {
            LazyColumn {
                items(wallets) { wallet ->
                    WalletCard(wallet)
                }
            }
        }

        Spacer(modifier = Modifier.height(16.dp))

        Button(
            onClick = { viewModel.showUserProfile() },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Show Profile")
        }

        Button(
            onClick = { viewModel.logout() },
            modifier = Modifier.fillMaxWidth(),
            colors = ButtonDefaults.buttonColors(
                containerColor = MaterialTheme.colorScheme.error
            )
        ) {
            Text("Logout")
        }
    }
}

@Composable
fun WalletCard(wallet: BaseWallet) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(vertical = 8.dp)
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(
                text = wallet.chain.uppercase(),
                style = MaterialTheme.typography.labelMedium,
                color = MaterialTheme.colorScheme.primary
            )
            Text(
                text = wallet.address,
                style = MaterialTheme.typography.bodyMedium
            )
        }
    }
}
```

## Enable Features in Dashboard

Before you can use authentication and wallet features, enable them in your Dynamic dashboard:

1. Go to your [Dynamic Dashboard](https://app.dynamic.xyz/dashboard/overview)
2. Select your project
3. Go to **Authentication** and enable the methods you want to use:
   * Email OTP
   * SMS OTP
   * Social providers (Google, Apple, Farcaster, etc.)
   * Passkeys
4. Go to **Wallets** and enable embedded wallets
5. Go to **Chains** and enable the networks you want to support (EVM and/or Solana)

<Tip>
  For testing, we recommend starting with Email OTP authentication and an EVM
  testnet like Base Sepolia. This gives you a complete setup without requiring
  real phone numbers or mainnet transactions.
</Tip>

## Troubleshooting

### Common Issues

* **Could not resolve: dynamic-sdk-android.aar**
  * Make sure you've placed the AAR file in your `app/libs` directory
  * Try cleaning the build (Build → Clean Project)
  * Restart Android Studio

* **SDK not initialized**
  * Ensure `DynamicSDK.initialize()` is called in your `MainActivity.onCreate()` before any views access the SDK
  * Don't call `DynamicSDK.getInstance()` before initialization

* **Authentication callbacks not working**
  * Verify your URL scheme is configured in `AndroidManifest.xml`
  * Ensure the `redirectUrl` matches your URL scheme
  * Whitelist your deep link URL in the Dynamic dashboard under **Security → Whitelist Mobile Deeplink**

* **Wallets not appearing after login**
  * Wallets are created asynchronously after authentication
  * Use `sdk.wallets.userWalletsChanges` Flow to listen for wallet updates
  * Check that embedded wallets are enabled in your dashboard

* **DynamicUI overlay not showing**
  * Make sure `DynamicUI()` composable is included in your app's root layout
  * Verify it's placed inside a `Box` or similar container that allows overlays

## Next Steps

<Warning>
  **Step-up authentication** and **device registration** are required
  before accepting the `2026_04_01` API version. You must implement
  both manually in your headless integration.
  See the [upgrade guide](/overview/migrations/api/2026_04_01).
</Warning>

Your SDK is configured. You can go deeper here:

1. **[Authentication Guide](/kotlin/headless-authentication)** - Learn how to implement user authentication with email, SMS, and passkeys
2. **[Social Authentication](/kotlin/social-authentication)** - Add social login options like Google, Apple, and Farcaster
3. **[Session Management](/kotlin/session-management)** - Manage authentication state with Kotlin Flow
4. **[Wallet Operations](/kotlin/wallets/general/token-balances)** - Work with balances and sign messages
5. **[EVM Operations](/kotlin/wallets/evm/send-eth)** - Perform EVM transactions
6. **[Solana Operations](/kotlin/wallets/solana/connection)** - Send Solana transactions
7. **[Connect to dApps](/kotlin/wallets/global-wallets/walletconnect)** - Use WalletConnect to connect your embedded wallet to any dApp
8. **[Step-up authentication](/kotlin/step-up-auth)** — required before accepting the `2026_04_01` API version
9. **[Device registration](/kotlin/device-registration)** — required before accepting the `2026_04_01` API version

<Info>
  Building without DynamicUI()? See the [Authentication screens](/kotlin/headless-authentication) for a full list of screens your app needs to handle.
</Info>
