Android SDK 28+ (Android 9.0+)
Kotlin 2.1.0+
Gradle 8.x+
Java 17
Environment ID from Dynamic Dashboard
Install the SDK
Step 1: Download AAR files
Download the latest AAR files from the Dynamic Android SDK repository :
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:
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" )
}
If you don’t need Solana support, you can skip the solana-web3.aar and sol4k dependencies.
Add the required permissions and activities to your AndroidManifest.xml:
< 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 >
Make sure the android:scheme in your manifest matches the redirectUrl in your ClientProps. For example, if redirectUrl = "myapp://", then use android:scheme="myapp".
Initialize the SDK
Initialize the Dynamic SDK in your MainActivity:
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 environmentIdYour Dynamic environment ID from the dashboard Yes appNameYour app’s display name Yes appLogoUrlURL to your app’s logo (shown in auth UI) Yes redirectUrlDeep link URL scheme for OAuth callbacks Yes appOriginYour app’s origin URL Yes logLevelLogging level (.DEBUG, .INFO, .WARN, .ERROR) No
Access the SDK
After initialization, access the SDK singleton anywhere in your app:
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:
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:
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
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:
Go to your Dynamic Dashboard
Select your project
Go to Authentication and enable the methods you want to use:
Email OTP
SMS OTP
Social providers (Google, Apple, Farcaster, etc.)
Passkeys
Go to Wallets and enable embedded wallets
Go to Chains and enable the networks you want to support (EVM and/or Solana)
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.
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
What’s Next
Great! Your SDK is now configured and ready to use. Here’s what you can do next:
Authentication Guide - Learn how to implement user authentication with email, SMS, and passkeys
Social Authentication - Add social login options like Google, Apple, and Farcaster
Session Management - Manage authentication state with Kotlin Flow
Wallet Operations - Work with balances and sign messages
EVM Operations - Perform EVM transactions
Solana Operations - Send Solana transactions
📱 Complete Example App : For a fully functional Android app demonstrating all SDK
features, check out our Android SDK Example App . It
includes authentication flows (email/SMS OTP, social login, passkeys), wallet management, EVM/Solana transactions,
MFA, and passkey examples with Jetpack Compose.