Skip to main content

Overview

The Dynamic Flutter SDK provides a comprehensive API for building Web3-enabled Flutter applications. This reference documents all the modules, functions, and types available in the SDK.

Requirements

  • Flutter 3.0+
  • Dart 3.0+
  • iOS 13.0+ / Android API Level 26+

Installation

# pubspec.yaml
dependencies:
  dynamic_sdk: ^VERSION

SDK Architecture

The SDK uses a singleton pattern with modular access to different functionalities:
import 'package:dynamic_sdk/dynamic_sdk.dart';

// Initialize at app launch (in main.dart)
void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await DynamicSDK.instance.initialize(
    environmentId: 'YOUR_ENV_ID',
  );

  runApp(MyApp());
}

// Access SDK instance
final sdk = DynamicSDK.instance;

// Access modules
sdk.auth       // Authentication
sdk.wallets    // Wallet management
sdk.evm        // EVM chain operations
sdk.solana     // Solana operations
sdk.networks   // Available networks
sdk.mfa        // Multi-factor authentication
sdk.passkeys   // Passkey management
sdk.ui         // Built-in UI components

Core Modules

Initialization

// Initialize SDK (call once at app launch)
await DynamicSDK.instance.initialize(
  environmentId: 'YOUR_ENV_ID',
);

// Get SDK instance
final sdk = DynamicSDK.instance;

Authentication (sdk.auth)

// Current user
final user = sdk.auth.authenticatedUser;

// Auth token
final token = sdk.auth.token;

// Reactive streams
sdk.auth.authenticatedUserChanges  // Stream<UserProfile?>
sdk.auth.tokenChanges              // Stream<String?>

// Logout
await sdk.auth.logout();

// Email OTP
await sdk.auth.email.sendOTP('[email protected]');
await sdk.auth.email.verifyOTP('123456');
await sdk.auth.email.resendOTP();

// SMS OTP
final phoneData = PhoneData(
  dialCode: '+1',
  iso2: 'US',
  phone: '5551234567',
);
await sdk.auth.sms.sendOTP(phoneData);
await sdk.auth.sms.verifyOTP('123456');
await sdk.auth.sms.resendOTP();

// Social authentication
await sdk.auth.social.connect(SocialProvider.google);
await sdk.auth.social.connect(SocialProvider.apple);
await sdk.auth.social.connect(SocialProvider.farcaster);

// Passkey authentication
await sdk.auth.passkey.signIn();

// External JWT
await sdk.auth.externalAuth.signInWithExternalJwt(
  jwt: 'your-jwt',
);

Wallets (sdk.wallets)

// Current wallets
final wallets = sdk.wallets.userWallets;  // List<BaseWallet>

// Reactive stream
sdk.wallets.userWalletsChanges  // Stream<List<BaseWallet>>

// Get balance
final balance = await sdk.wallets.getBalance(wallet);

// Get current network
final network = await sdk.wallets.getNetwork(wallet);

// Switch network
await sdk.wallets.switchNetwork(wallet, targetNetwork);

// Set primary wallet
await sdk.wallets.setPrimary(walletId);

// Sign message
final signature = await sdk.wallets.signMessage(wallet, 'Hello');

// Sign typed data (EIP-712)
final signature = await sdk.wallets.signTypedData(wallet, typedDataJson);

// Verify signature
final isValid = await sdk.wallets.verifySignature(
  wallet: wallet,
  message: 'Hello',
  signature: signature,
);

EVM Operations (sdk.evm)

// Create public client for chain
final client = sdk.evm.createPublicClient(chainId: 1);

// Get gas price
final gasPrice = await client.getGasPrice();

// Send transaction
final transaction = EthereumTransaction(
  to: '0x...',
  value: '1000000000000000',  // Wei as String
  gasLimit: 21000,
  maxFeePerGas: gasPrice.maxFeePerGas,
  maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas,
);
final txHash = await sdk.evm.sendTransaction(wallet, transaction);

// Sign transaction (without sending)
final signedTx = await sdk.evm.signTransaction(wallet, transaction);

// Write to contract
final input = WriteContractInput(
  address: '0x...',
  functionName: 'transfer',
  args: [recipient, amount],
  abi: [/* ABI maps */],
);
final txHash = await sdk.evm.writeContract(wallet, input);

Solana Operations (sdk.solana)

// Create connection
final connection = sdk.solana.createConnection();

// Get latest blockhash
final blockhashResult = await connection.getLatestBlockhash();
final blockhash = blockhashResult.blockhash;

// Create signer
final signer = sdk.solana.createSigner(wallet);

// Sign message
final signature = await signer.signMessage('Hello');

// Sign transaction
final signedTx = await signer.signEncodedTransaction(base64Transaction);

// Sign and send transaction
final signature = await signer.signAndSendEncodedTransaction(base64Transaction);

Networks (sdk.networks)

// Available EVM networks
final evmNetworks = sdk.networks.evm;  // List<GenericNetwork>

// Available Solana networks
final solanaNetworks = sdk.networks.solana;  // List<GenericNetwork>

// Network properties
network.name        // String
network.chainId     // For EVM
network.networkId   // For Solana

MFA (sdk.mfa)

// Get user devices
final devices = await sdk.mfa.getUserDevices();

// Add TOTP device (returns MfaAddDevice with secret)
final device = await sdk.mfa.addDevice('totp');

// Verify device
await sdk.mfa.verifyDevice('123456', 'totp');

// Authenticate device to get MFA token
final token = await sdk.mfa.authenticateDevice(
  code: '123456',
  deviceId: deviceId,
  createMfaToken: MfaCreateToken(singleUse: true),
);

// Delete device (requires MFA token)
await sdk.mfa.deleteUserDevice(deviceId, mfaAuthToken: token);

// Recovery codes
final codes = await sdk.mfa.getRecoveryCodes(generateNewCodes: false);
await sdk.mfa.acknowledgeRecoveryCodes();  // Mark codes as shown to user
await sdk.mfa.authenticateRecoveryCode(recoveryCode);

Passkeys (sdk.passkeys)

// Get user's passkeys
final passkeys = await sdk.passkeys.getPasskeys();

// Register new passkey
await sdk.passkeys.registerPasskey();

// Authenticate with passkey for MFA
final response = await sdk.passkeys.authenticatePasskeyMFA(
  createMfaToken: MfaCreateToken(singleUse: true),
  relatedOriginRpId: null,
);

// Delete passkey
await sdk.passkeys.deletePasskey(
  passkeyId: passkeyId,
);

// Passkey properties
passkey.id              // String - Unique identifier
passkey.createdAt       // String - Creation timestamp (ISO format)
passkey.lastUsedAt      // String? - Last authentication time
passkey.isDefault       // bool? - Whether this is the default passkey

Built-in UI (sdk.ui)

// Show authentication UI
await sdk.ui.showAuth();

// Show user profile
await sdk.ui.showUserProfile();

Data Types

DynamicSDKConfig

// Configuration is passed to initialize method
await DynamicSDK.instance.initialize(
  environmentId: 'YOUR_ENV_ID',  // Required
);

BaseWallet

class BaseWallet {
  final String address;      // Wallet address
  final String chain;        // "EVM" or "SOL"
  final String? walletName;  // Wallet name
  final String? id;          // Wallet ID for API operations
}

UserProfile

class UserProfile {
  final String? userId;
  final String? email;
  final String? phoneNumber;
  // Additional properties available
}

PhoneData

class PhoneData {
  final String dialCode;   // e.g., "+1"
  final String iso2;       // e.g., "US"
  final String phone;      // Phone number without country code
}

WriteContractInput

class WriteContractInput {
  final String address;
  final String functionName;
  final List<dynamic> args;
  final List<Map<String, dynamic>> abi;
}

GenericNetwork

class GenericNetwork {
  final String name;
  final int? chainId;      // For EVM networks
  final String? networkId; // For Solana networks
}

Error Handling

All SDK functions can throw exceptions. Use Dart’s native error handling:
try {
  await sdk.auth.email.verifyOTP(code);
} catch (e) {
  print('Error: $e');
}
For async operations:
try {
  await sdk.auth.email.sendOTP(email);
} catch (e) {
  setState(() {
    errorMessage = 'Failed: $e';
  });
}

Quick Reference Example

import 'package:flutter/material.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await DynamicSDK.instance.initialize(
    environmentId: 'YOUR_ENV_ID',
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Dynamic SDK Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final sdk = DynamicSDK.instance;
  bool isAuthenticated = false;

  @override
  void initState() {
    super.initState();

    // Check initial auth state
    isAuthenticated = sdk.auth.authenticatedUser != null;

    // Listen to auth changes
    sdk.auth.authenticatedUserChanges.listen((user) {
      setState(() {
        isAuthenticated = user != null;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Dynamic SDK Demo'),
      ),
      body: Center(
        child: isAuthenticated ? HomeScreen() : LoginScreen(),
      ),
    );
  }
}

class LoginScreen extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async {
        try {
          await sdk.ui.showAuth();
        } catch (e) {
          print('Login failed: $e');
        }
      },
      child: Text('Sign In'),
    );
  }
}

class HomeScreen extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Text('Welcome!'),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () async {
            try {
              await sdk.ui.showUserProfile();
            } catch (e) {
              print('Error: $e');
            }
          },
          child: Text('View Profile'),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () async {
            try {
              await sdk.auth.logout();
            } catch (e) {
              print('Logout failed: $e');
            }
          },
          child: Text('Logout'),
        ),
      ],
    );
  }
}

Session State Management with StreamBuilder

For production apps, use StreamBuilder for reactive state management:
import 'package:flutter/material.dart';
import 'package:dynamic_sdk/dynamic_sdk.dart';

class AppContent extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<UserProfile?>(
      stream: sdk.auth.authenticatedUserChanges,
      initialData: sdk.auth.authenticatedUser,
      builder: (context, snapshot) {
        final isAuthenticated = snapshot.data != null;

        if (isAuthenticated) {
          return HomeScreen();
        } else {
          return LoginScreen();
        }
      },
    );
  }
}

// Wallet management with StreamBuilder
class WalletList extends StatelessWidget {
  final sdk = DynamicSDK.instance;

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<List<BaseWallet>>(
      stream: sdk.wallets.userWalletsChanges,
      initialData: sdk.wallets.userWallets,
      builder: (context, snapshot) {
        final wallets = snapshot.data ?? [];

        if (wallets.isEmpty) {
          return Text('No wallets found');
        }

        return ListView.builder(
          itemCount: wallets.length,
          itemBuilder: (context, index) {
            final wallet = wallets[index];
            return ListTile(
              title: Text(wallet.address),
              subtitle: Text(wallet.chain),
              trailing: Text(wallet.walletName ?? 'Unknown'),
            );
          },
        );
      },
    );
  }
}

Using FutureBuilder for Async Operations

class WalletBalance extends StatelessWidget {
  final BaseWallet wallet;
  final sdk = DynamicSDK.instance;

  WalletBalance({required this.wallet});

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String>(
      future: sdk.wallets.getBalance(wallet),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        }

        if (snapshot.hasError) {
          return Text('Error: ${snapshot.error}');
        }

        return Text('Balance: ${snapshot.data}');
      },
    );
  }
}
Complete Example App: For a fully functional Flutter app demonstrating all SDK features, check the example directory in the dynamic_sdk package for complete implementation examples.