The Para Flutter SDK enables you to integrate secure wallet features including creation, passkey-based authentication, and transaction signing into your mobile applications. This guide covers all necessary steps from installation to implementing authentication flows.

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.

Install the SDK

Start by installing the Para SDK:
flutter pub add para

Configure URL Scheme

Configure your app’s URL scheme for OAuth authentication flows. This enables OAuth providers to redirect back to your app after authentication.
  1. In Xcode, select your project in the navigator
  2. Select your app target
  3. Go to the Info tab
  4. Scroll down to URL Types and click + to add a new URL type
  5. Fill in the fields:
    • URL Schemes: Enter your scheme name (e.g., yourapp, myflutterapp)
    • Role: Select Editor
    • Identifier: Use your bundle identifier or a descriptive name
Make sure your URL scheme is unique to avoid conflicts with other apps. Use a scheme related to your app’s bundle ID (e.g., com.mycompany.myapp) for uniqueness.

Configure Associated Domains for Passkeys

To enable passkeys in your Flutter project, you need to configure both iOS and Android platforms:
To enable passkeys on iOS, you need to configure Associated Domains:
  1. Open your Flutter project’s iOS folder in Xcode
  2. In Xcode, go to Signing & Capabilities for your app target
  3. Click + Capability and add Associated Domains
  4. Add the following entries:
    webcredentials:app.usecapsule.com
    webcredentials:app.beta.usecapsule.com
    
  5. Register your Team ID + Bundle ID with Para via the Developer Portal
Without properly registering your Team ID and Bundle ID with Para, passkey authentication flows will fail. Contact Para support if you encounter issues with passkey registration.

Initialize 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:
lib/services/para_client.dart
import 'package:para/para.dart';

// Para Configuration
final config = ParaConfig(
  apiKey: 'YOUR_PARA_API_KEY', // Get from: https://developer.getpara.com
  environment: Environment.beta, // Use Environment.prod for production
);

// Initialize Para client instance using v2 factory constructor
final para = Para.fromConfig(
  config: config,
  appScheme: 'yourapp', // Your app's scheme (without ://)
);
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.

Authenticate Users

Para provides a unified authentication experience that supports email, phone, and social login methods. The SDK automatically determines whether a user is new or existing and guides you through the appropriate flow.
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.

Build Your Authentication Flow

The recommended approach is to create a single authentication view that handles all authentication methods together. This provides users with all authentication options in one place.
Para supports authentication with both email addresses and phone numbers.
1

Handle Email/Phone Submission

Initiate authentication with email or phone:
lib/views/authentication_view.dart
// Determine if input is email or phone
final Auth auth;
if (userInput.contains('@')) {
  auth = Auth.email(userInput);
} else {
  auth = Auth.phone(userInput); // Include country code
}

// SDK call: Initiate authentication
final authState = await para.initiateAuthFlow(auth: auth);

// Handle the result based on stage
switch (authState.stage) {
  case AuthStage.verify:
    // New user - needs verification (see OTP handling step below)
    break;
  case AuthStage.login:
    // Existing user - complete login
    await para.handleLogin(
      authState: authState,
    );
    break;
  case AuthStage.signup:
    // Handle signup after verification
    break;
}
2

Handle OTP Verification

For new users who need verification, handle the OTP flow:
lib/views/authentication_view.dart
// After user enters the verification code
final verificationCode = "123456"; // Get from user input

try {
  // Submit verification code
  final verifiedState = await para.verifyOtp(
    otp: verificationCode
  );
  
  // Complete signup with passkey
  await para.handleSignup(
    authState: verifiedState,
    signupMethod: SignupMethod.passkey,
  );
  
  // User is now signed up and authenticated
} catch (e) {
  // Handle error
}

Handle Returning Users

After initial setup, users can log in using their email or phone with the same authentication flow:
lib/views/login_view.dart
// Login with email
final authState = await para.initiateAuthFlow(
  auth: Auth.email(userEmail)
);

// Since the user exists, authState.stage will be AuthStage.login
if (authState.stage == AuthStage.login) {
  await para.handleLogin(
    authState: authState,
  );
  // User is now logged in
}
Alternatively, you can use direct passkey login if you know the user has set up passkeys:
lib/views/login_view.dart
// Direct passkey login (will show all available passkeys if email/phone not provided)
await para.loginWithPasskey(
  email: userEmail, // Optional - helps filter passkeys
  phone: null
);
This will prompt the user for biometric verification before granting access to their wallets.

Check Authentication Status

You can check if a user is already authenticated:
lib/views/content_view.dart
final isLoggedIn = await para.isSessionActive().future;

if (isLoggedIn) {
  // User is authenticated, proceed to main app flow
} else {
  // Show login/signup UI
}

Sign Out Users

To sign out a user and clear their session:
lib/views/settings_view.dart
await para.logout().future;

Create and Manage Wallets

After successful authentication, you can perform wallet operations:
lib/views/wallet_view.dart
// Get all user wallets
await para.fetchWallets(); // Ensure we have the latest wallets

final wallets = await para.fetchWallets().future;

if (wallets.isEmpty) {
  // No wallets, perhaps create one
  final wallet = await para.createWallet(
    type: WalletType.evm,
    skipDistribute: false,
  ).future;
  
  print('Created wallet: ${wallet.address}');
  
  // Sign a simple message (SDK handles Base64 encoding internally)
  final signature = await para.signMessage(
    walletId: wallet.id,
    messageBase64: base64Encode(utf8.encode('Hello, Para!')),
  ).future;
  print('Signature: ${signature.signature}');
  
} else {
  // Use existing wallet
  final firstWallet = wallets.first;
  
  // Sign a simple message (SDK handles Base64 encoding internally)
  final signature = await para.signMessage(
    walletId: firstWallet.id,
    messageBase64: base64Encode(utf8.encode('Hello, Para!')),
  ).future;
  print('Signature: ${signature.signature}');
}
For detailed transaction signing with specific blockchains (EVM, Solana, Cosmos), please refer to the respective blockchain integration guides.

Example

For a complete implementation example, check out our Flutter SDK example app:

Flutter SDK Example App

Next Steps

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