This guide walks you through the setup of Para in a Flutter application. You’ll learn how to install the SDK, configure iOS/Android for passkey-based logins, implement user authentication flows, and generate wallets.

If you haven’t already created your Flutter app, follow the official Flutter docs to set up a new project.

Prerequisites

To use Para, you need an API key. This key authenticates your requests to Para services and is essential for integration.

Don’t have an API key yet? Request access to the Developer Portal to create API keys, manage billing, teams, and more.

Installation

Start by installing the Para SDK:

flutter pub add para

Project Setup

To set up associated domains for passkey functionality in your Flutter project, you need to configure both iOS and Android platforms:

To enable passkeys on iOS, you need to set up associated domains in your Xcode project:

  1. Open your Flutter project’s iOS folder in Xcode
  2. Select your target and go to “Signing & Capabilities”
  3. Click ”+ Capability” and add “Associated Domains”
  4. Add the following domains:
    • webcredentials:app.beta.usecapsule.com
    • webcredentials:app.usecapsule.com

For more details, see the Apple Developer documentation.

Important: You must register your TeamId and BundleId with Para via the Developer Portal to use associated domains. This is required by Apple for passkey security. Allow up to 24 hours for domain propagation.

Initializing Para

To use Para’s features, you’ll need to initialize a Para client instance that can be accessed throughout your app. This client handles all interactions with Para’s services, including authentication, wallet management, and transaction signing.

Create a file (e.g., lib/services/para_client.dart) to initialize your Para client:

import 'package:para/para.dart';

// Initialize a global Para client instance
final para = Para(
  environment: Environment.beta, // Use Environment.prod for production
  apiKey: 'YOUR_PARA_API_KEY',
  deepLinkScheme: 'yourapp://callback', // Required for OAuth/WebAuth
);

You can access para from anywhere in your app by importing the file where you initialized it. This singleton pattern ensures consistent state management across your application.

Para offers two hosted environments: Environment.BETA (alias Environment.DEVELOPMENT) for testing, and Environment.PROD (alias Environment.PRODUCTION) for live use. Select the environment that matches your current development phase.

Using the Para SDK

The Para SDK v2 provides a unified authentication flow that automatically handles both new and existing users. Here’s how to implement authentication in your Flutter application.

Beta Testing Credentials In the BETA Environment, you can use any email ending in @test.getpara.com (like dev@test.getpara.com) or US phone numbers (+1) in the format (area code)-555-xxxx (like (425)-555-1234). Any OTP code will work for verification with these test credentials. These credentials are for beta testing only. You can delete test users anytime in the beta developer console to free up user slots.

WebAuthenticationSession Setup

For authentication methods that require web flows (password authentication), you’ll need to create a FlutterWebAuthSession:

import 'package:para/para.dart';

// Create a web authentication session
final webAuthSession = FlutterWebAuthSession(
  callbackUrlScheme: 'yourapp', // Your app's URL scheme
);

Native passkey flows (loginWithPasskey, generatePasskey) do NOT use WebAuthenticationSession - they use the device’s native passkey prompts directly.

Email Authentication Flow

The V2 SDK provides a streamlined authentication flow that handles both new and existing users:

import 'package:para/para.dart';
import 'package:para/src/auth_extensions.dart'; // For Auth.email helper

Future<void> authenticateWithEmail(String email) async {
  // Step 1: Initiate auth flow (using extension method)
  final authState = await para.initiateAuthFlow(
    auth: Auth.email(email)
  );
  
  // Or call the core method directly:
  // final authStateFuture = para.signUpOrLogIn(auth: {'email': email});
  // final authState = await authStateFuture.future;
  
  // Step 2: Handle based on auth stage
  switch (authState.stage) {
    case AuthStage.verify:
      // New user - needs verification
      print('Verification code sent to $email');
      // Show UI to collect verification code
      break;
      
    case AuthStage.login:
      // Existing user - proceed to login
      await handleLogin(authState);
      break;
      
    case AuthStage.signup:
      // Should not happen directly, comes after verification
      break;
  }
}

Future<void> verifyEmail(String verificationCode) async {
  // Verify the code
  final verifiedStateFuture = para.verifyNewAccount(
    verificationCode: verificationCode
  );
  final verifiedState = await verifiedStateFuture.future;
  
  // After verification, proceed to signup
  if (verifiedState.stage == AuthStage.signup) {
    await handleSignup(verifiedState);
  }
}

Future<void> handleLogin(AuthState authState) async {
  // Login with passkey - no webAuthSession needed
  final walletFuture = para.loginWithPasskey(
    email: authState.email,
  );
  final wallet = await walletFuture.future;
  
  // User is now logged in
  final walletsFuture = para.fetchWallets();
  final wallets = await walletsFuture.future;
}

Future<void> handleSignup(AuthState authState) async {
  // For simplified flow, use the auth extension
  final wallet = await para.handleSignup(
    authState: authState,
    method: SignupMethod.passkey,
  );
  
  print('Wallet created: ${wallet.address}');
  
  // Alternative: Manual flow
  // 1. Generate passkey
  // await para.generatePasskey();
  // 2. Create wallet
  // final wallet = await para.createWallet(
  //   type: WalletType.evm,
  //   skipDistribute: false,
  // ).future;
}

Session Management

Check if a user is logged in and manage their session:

// Check if session is active
Future<void> checkSession() async {
  final isActive = await para.isSessionActive().future;
  
  if (isActive) {
    // User is logged in
    final wallets = await para.fetchWallets().future;
    final authDetails = await para.getCurrentUserAuthDetails().future;
  }
}

// Export session for backup
Future<void> exportUserSession() async {
  final sessionData = await para.exportSession().future;
  // Store sessionData securely
}

// Logout
Future<void> logoutUser() async {
  await para.logout().future;
}

Working with Wallets

Once authenticated, you can access and manage wallets:

// Get all wallets
Future<void> getUserWallets() async {
  final wallets = await para.fetchWallets();
  
  for (final wallet in wallets) {
    print('Type: ${wallet.type}'); // WalletType.evm, .solana, or .cosmos
    print('Address: ${wallet.address}');
    print('Scheme: ${wallet.scheme}'); // WalletScheme.mpc
  }
}

// Get specific wallet by filtering
Future<Wallet?> getSpecificWallet(String walletId) async {
  final walletsFuture = para.fetchWallets();
  final wallets = await walletsFuture.future;
  
  try {
    return wallets.firstWhere((w) => w.id == walletId);
  } catch (e) {
    return null; // Not found
  }
}

// Check if using external wallet
Future<void> checkExternalWallet() async {
  final isExternal = await para.isUsingExternalWallet();
  if (isExternal) {
    print('User is using an external wallet');
  }
}

Cancellable Operations

Many Para operations return a ParaFuture that can be cancelled:

// Example with signing
final signatureFuture = para.signMessage(
  walletId: wallet.id,
  messageBase64: base64Encode(utf8.encode('Hello!')),
);

// Cancel if needed
await para.cancelOperationById(signatureFuture.requestId);

// Or await the result
try {
  final signature = await signatureFuture.future;
  print('Signature: ${signature.signature}');
} catch (e) {
  // Handle cancellation or error
}

Examples

For practical implementations of the Para SDK in Flutter environments, check out our GitHub repository:

Troubleshooting

If you encounter issues during the integration or usage of the Para SDK in your Flutter application, here are some common problems and their solutions:

Next Steps

After integrating Para into your Flutter app, you can explore other features and integrations to enhance your Para experience.