Please be aware that our Flutter SDK is in open alpha at the moment!

Available now:

  • email login (headless)
  • sms login (headless)
  • auth flow UI
  • profile UI
  • EVM embedded wallets
  • web3dart integration

Coming next:

  • social login
  • solana embedded wallets

Installation

Simply run the following in your terminal:

flutter pub add dynamic_sdk

This will add a line like this to your package’s pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  dynamic_sdk: ^0.0.1-alpha.2

Set up

Getting started with DynamicSDK takes only three steps:

1. Initialize your client

First you have to start the client singleton with your data in ClientProps;

import 'package:dynamic_sdk/dynamic_sdk.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  DynamicSDK.init(
    props: ClientProps(
      environmentId: 'your-environment-id',
      appLogoUrl: 'your-logo-url',
      appName: 'your-app-name',
    ),
  );
  runApp(const MyApp());
}

2. Wait for the SDK to load

Add the DynamicSDK.instance.dynamicWidget and wait for the SDK to loaded using the DynamicSDK.instance.sdk.readyChanges stream;

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Stack(
        children: [
          // Make sure the SDK is ready before using it
          StreamBuilder<bool?>(
            stream: DynamicSDK.instance.sdk.readyChanges,
            builder: (context, snapshot) {
              final sdkReady = snapshot.data ?? false;
              return sdkReady
                  ? const MyHomePage(title: 'Flutter Demo Home Page')
                  : const SizedBox.shrink();
            },
          ),
          // DynamicSDK widget must be available all the time
          DynamicSDK.instance.dynamicWidget,
        ],
      ),
    );
  }
}

3. Do your stuff

That’s it! Now you are good to go! See below how to authenticate using our UI:

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: SingleChildScrollView(
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Column(
                children: [
                  // Listen to auth token changes
                  StreamBuilder<String?>(
                    stream: DynamicSDK.instance.auth.tokenChanges,
                    builder: (context, snapshot) {
                      final authToken = snapshot.data;
                      // Show the auth token when logged in
                      return authToken != null
                          ? Column(
                              children: [
                                const LogoutButton(),
                                const SizedBox(height: 24),
                                Text('AUTH TOKEN: $authToken'),
                              ],
                            )
                          // Show Dynamic UI for sign in
                          : const LoginButton();
                    },
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// Show Dynamic UI for sign in
class LoginButton extends StatelessWidget {
  const LoginButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showAuth(),
      child: const Text('Dynamic Login'),
    );
  }
}

// Headless logout function
class LogoutButton extends StatelessWidget {
  const LogoutButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.auth.logout(),
      child: const Text('Logout'),
    );
  }
}

Available modules

Let’s walk you through the available modules and how to use most of the features.

SDK Module

  1. Get to know when the SDK is ready to be used by listening to readyChanges stream.
StreamBuilder<bool?>(
  stream: DynamicSDK.instance.sdk.readyChanges,
  builder: (context, snapshot) {
    final sdkReady = snapshot.data ?? false;
    return sdkReady
        ? const MyHomePage(title: 'Flutter Demo Home Page')
        : const SizedBox.shrink();
  },
),

User Interface Module

  1. Use our interface to sign in
class LoginButton extends StatelessWidget {
  const LoginButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showAuth(),
      child: const Text('Dynamic Login'),
    );
  }
}
  1. Use our interface to see the user’s profile
class UserProfileButton extends StatelessWidget {
  const UserProfileButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () => DynamicSDK.instance.ui.showUserProfile(),
      child: const Text('Show Profile'),
    );
  }
}

Auth Module (headless authentication)

  1. Headless e-mail sign in
class EmailLoginButton extends StatelessWidget {
  const EmailLoginButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async => await DynamicSDK.instance.auth.email.sendOTP(
        'user@email.com'
      ),
      child: const Text('Email Login'),
    );
  }
}
  1. Headless SMS sign in
class SMSLoginButton extends StatelessWidget {
  const SMSLoginButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () async => await DynamicSDK.instance.auth.sms.sendOTP(
        PhoneData(
          phone: phone, // User's phone number
          iso2: 'US',
          dialCode: '+1',
        ),
      ),
      child: const Text('SMS Login'),
    );
  }
}
  1. Verity OTP
class VerifyOTPButton extends StatelessWidget {
  const VerifyOTPButton({
    super.key,
  });

  
  Widget build(BuildContext context) {
    return ElevatedButton(
      // For email use await DynamicSDK.instance.auth.email.verifyOTP(code),
      onPressed: () async => await DynamicSDK.instance.auth.sms.verifyOTP(code), // Verification code
      child: const Text('Verify Code'),
    );
  }
}

Wallets Module

  1. Get network
Future<String> getNetworkInfo({required BaseWallet wallet}) async {
  final network = await DynamicSDK.instance.wallets.getNetwork(wallet: wallet);
  final name = getNetworkName(network.value);

  return name;
}

String getNetworkName(networkId) {
  final evm = DynamicSDK.instance.networks.evm;

  bool isEvm = evm.any((network) => network.networkId == networkId);

  if (isEvm) {
    final network = evm.firstWhere(
      (network) {
        return network.networkId == networkId;
      },
    );

    return network.name;
  } else {
    return networkId;
  }
}
  1. Switch networks
void switchNetwork({
  required BaseWallet wallet,
  required int chainId,
}) async {
  await DynamicSDK.instance.wallets.switchNetwork(
    wallet: wallet,
    network: Network(chainId),
  );
}
  1. Sign message
Future<String?> signMessage({
  required String message,
  required BaseWallet wallet,
}) async {
  try {
    final signedMessage = await DynamicSDK.instance.wallets.signMessage(
      message: message,
      wallet: wallet,
    );
    
    return signedMessage;
  } catch (e) {
    print(e);
    rethrow;
  }
}

Other methods like getBalance and setPrimary are as straight forward as the ones above.

You can read more about our client package here.