ZeroXKey SDK Flutter
The zeroxkey_sdk_flutter package simplifies the integration of the ZeroXKey API into Flutter applications. It provides secure session management, authentication, and cryptographic operations using flutter_secure_storage, zeroxkey_crypto, zeroxkey_api_key_stamper and zeroxkey_http
This package is a thin Flutter presentation layer over zeroxkey_core,
which owns the platform-agnostic architecture (errors, configuration, network
middleware, signing ports, repositories, use cases, and the DI composition
root). ZeroXKeyProvider keeps its public method/getter contract unchanged and
delegates to the core graph; Flutter-specific concerns (secure storage, Hive
session storage, OAuth redirect handling) are implemented here as adapters.
Note: the top-level export surface is intentionally narrow. The generated HTTP client and the transport/base internals are no longer re-exported; protocol DTO models, configuration types, and helpers such as
isValidSessionremain available.
Example App

For a fully functional Flutter demo app that leverages ZeroXKey's Dart/Flutter packages, check out our ZeroXKey Flutter Demo App.
Installation
Add the following dependencies to your Flutter project:
flutter pub add zeroxkey_sdk_flutter
Ensure your app is properly configured for secure storage and deep linking for OAuth redirects (if applicable).
Usage
Wrapping Your App with the Provider
Wrap your root app with ZeroXKeyProvider using provider:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:zeroxkey_sdk_flutter/zeroxkey_sdk_flutter.dart';
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => ZeroXKeyProvider(
config: ZeroXKeyConfig(
apiBaseUrl: '<your_api_base_url>',
organizationId: '<your_organization_id>',
appScheme: '<your_app_scheme>',
onSessionCreated: (session) => print('Session created: ${session.key}'),
onSessionSelected: (session) => print('Session selected: ${session.key}'),
onSessionExpired: (session) => print('Session expired: ${session.key}'),
onSessionCleared: (session) => print('Session cleared: ${session.key}'),
onInitialized: (err) => print(err ?? 'Initialized successfully'),
),
),
),
],
child: const MyApp(),
),
);
}
Session Storage
Session data is stored across two layers:
Local Storage (Hive)
Used for non-sensitive session metadata such as JWT, expiry, organization, and active session keys.
| Key | Description |
|---|---|
@zeroxkey/all-session-keys |
List of all stored session keys |
@zeroxkey/active-session-key |
Tracks the currently active session |
@zeroxkey/session |
Stores session data and metadata from JWT |
Secure Storage (flutter_secure_storage)
Used for private cryptographic material, such as key pairs for signing.
| Key | Description |
|---|---|
@zeroxkey/embedded-key |
Embedded key pair used for signing |
<publicKey> |
Stores the private key associated with that public key |
Whats Provided by the ZeroXKey Provider?
Below is the full list of publicly exposed functions and state available from the ZeroXKeyProvider class:
Exposed State
These properties are automatically updated when you use the SDK functions:
| Property | Description |
|---|---|
session |
The currently active session, updated automatically on login, refresh, or clear. |
client |
The active ZeroXKeyClient instance tied to the current session. |
user |
The user data retrieved for the current session. Automatically refreshed after login or user changes. |
wallets |
The wallets fetched or created during user operations, refreshed when new wallets are added or imported. |
Exposed Functions
Session Management
storeSession({ required String sessionJwt, String? sessionKey })getSession({ String? sessionKey })getAllSessions()setActiveSession({ required String sessionKey })getActiveSessionKey()refreshSession({ String? sessionKey, String expirationSeconds, String? publicKey, bool invalidateExisting })clearSession({ String? sessionKey })clearAllSessions()createApiKeyPair({ String? externalPublicKey, String? externalPrivateKey, bool isCompressed, bool storeOverride })deleteApiKeyPair(String publicKey)deleteUnusedKeyPairs()ready— AFuturethat completes when initialization finishes.
User Management
refreshUser()
Wallet Management
refreshWallets()createWallet({ required String walletName, required List<v1WalletAccountParams> accounts, int? mnemonicLength })importWallet({ required String mnemonic, required String walletName, required List<v1WalletAccountParams> accounts, String? dangerouslyOverrideSignerPublicKey })exportWallet({ required String walletId, bool? returnMnemonic, String? dangerouslyOverrideSignerPublicKey })
Transaction Signing
signRawPayload({ required String signWith, required String payload, required v1PayloadEncoding encoding, required v1HashFunction hashFunction })signTransaction({ required String signWith, required String unsignedTransaction, required v1TransactionType type })
Authentication - OTP
initOtp({ required OtpType otpType, required String contact })verifyOtp({ required String otpCode, required String otpId, required String contact, required OtpType otpType })loginWithOtp({ required String verificationToken, String? organizationId, bool invalidateExisting, String? publicKey, String? sessionKey })signUpWithOtp({ required String verificationToken, required String contact, required OtpType otpType, String? publicKey, String? sessionKey, CreateSubOrgParams? createSubOrgParams, bool invalidateExisting })loginOrSignUpWithOtp({ required String otpId, required String otpCode, required String contact, required OtpType otpType, String? publicKey, bool invalidateExisting, String? sessionKey, CreateSubOrgParams? createSubOrgParams })
Authentication - OAuth
loginWithOAuth({ required String oidcToken, required String publicKey, bool? invalidateExisting, String? sessionKey })signUpWithOAuth({ required String oidcToken, required String publicKey, required String providerName, String? sessionKey, CreateSubOrgParams? createSubOrgParams })loginOrSignUpWithOAuth({ required String oidcToken, required String publicKey, String? providerName, String? sessionKey, bool? invalidateExisting, CreateSubOrgParams? createSubOrgParams })handleGoogleOAuth({ String? clientId, List<String>? secondaryClientIds, String? originUri, String? redirectUri, String? sessionKey, bool? invalidateExisting, void Function(String oidcToken)? onSuccess })handleAppleOAuth({ List<String>? secondaryClientIds, String? sessionKey, bool? invalidateExisting, void Function(String oidcToken)? onSuccess })— native iOS Apple Sign-In viasign_in_with_apple. Falls back to a web flow on Android (requiresserviceIdto be configured).handleAppleWebOAuth({ String? clientId, List<String>? secondaryClientIds, String? originUri, String? redirectUri, String? sessionKey, bool? invalidateExisting, void Function(String oidcToken)? onSuccess })— explicit web-based Apple OAuth (formerlyhandleAppleOAuth).handleXOAuth({ String? clientId, List<String>? secondaryClientIds, String? originUri, String? redirectUri, String? sessionKey, bool? invalidateExisting, void Function(String oidcToken)? onSuccess })handleDiscordOAuth({ String? clientId, List<String>? secondaryClientIds, String? originUri, String? redirectUri, String? sessionKey, bool? invalidateExisting, void Function(String oidcToken)? onSuccess })
Configuring OAuth providers
OAuthConfig(
providers: OAuthProviders(
google: GoogleOAuthProviderParams(
primaryClientId: GoogleOAuthPrimaryClientId(webClientId: '<your-google-web-client-id>'),
secondaryClientIds: const ['<additional-google-client-id>'],
),
apple: AppleOAuthProviderParams(
primaryClientId: AppleOAuthPrimaryClientId(
serviceId: '<your-apple-services-id>',
iosBundleId: '<your-ios-bundle-id>', // required for cross-platform iOS native compatibility
),
),
x: XOAuthProviderParams(primaryClientId: '<your-x-client-id>'),
discord: DiscordOAuthProviderParams(primaryClientId: '<your-discord-client-id>'),
),
)
Cross-platform OAuth (mobile + web)
secondaryClientIds on each provider lets you register additional client IDs as authenticators on the user's sub-organization at signup. This allows the same OAuth identity (e.g. one Google account) to resolve to the same ZeroXKey sub-organization regardless of which platform the user signs in from.
The SDK JWT-decodes the OIDC token from the primary signup to extract iss/sub, then synthesizes additional OauthProviderParams entries — one per secondary clientId — sharing the same identity. The result: the sub-org has multiple authenticators registered, one per audience.
Native Apple Sign-In
handleAppleOAuth uses native Apple Sign-In on iOS via sign_in_with_apple. Consumers must:
- Add the Sign in with Apple capability to the iOS Runner target in Xcode (Signing & Capabilities → + Capability → Sign in with Apple). This adds the
com.apple.developer.applesigninentitlement. - Configure
AppleOAuthProviderParams.primaryClientId.serviceIdif you wanthandleAppleOAuthto work on Android (the package falls back to a web flow there, which requires the Services ID). - For cross-platform compatibility — a user signing up on Android being able to log in via native Apple Sign-In on iOS, or vice versa — set both
serviceIdandiosBundleIdonAppleOAuthProviderParams. The SDK registers both as audiences on the sub-org at signup.
If you specifically want the web-based Apple flow on iOS instead of the native sheet, use handleAppleWebOAuth.
Authentication - Passkey
loginWithPasskey({ required String rpId, String? sessionKey, String expirationSeconds, String? organizationId, String? publicKey })signUpWithPasskey({ required String rpId, String? sessionKey, String expirationSeconds, String? organizationId, String? passkeyDisplayName, CreateSubOrgParams? createSubOrgParams, bool invalidateExisting })
❗Note: If a specific ZeroXKey action isn't listed here, it doesn't mean it's unsupported and it usually just means there's little benefit to providing a sugared wrapper for it. You can still use the exported
ZeroXKeyClientto call any ZeroXKey API endpoint directly whenever needed!
Handling Multiple Sessions
Most users won't need multiple sessions, but if your app requires switching between multiple sessions, here’s what you need to know:
This SDK supports multiple sessions, allowing you to create and switch between different session keys using setActiveSession({ sessionKey }). When a session is selected, the client, user, wallets, and session information are updated accordingly, so that all subsequent function calls (like importWallet or createWallet) apply to the selected session.
-
Creating a session with a custom key:
- You can pass a
sessionKeywhen callingstoreSession. If provided, the session will be stored in secure storage under that ID, allowing for multiple sessions.
- You can pass a
- Switching sessions: Use
setActiveSession({ sessionKey })to switch between stored sessions. The client, user, wallets, and session information will automatically update. - Session expiry management: Each session has an expiry time, and expired sessions will be automatically cleared.
- Callbacks for session events:
onSessionCreated: Called when a session is created.onSessionSelected: Called when a session is selected.onSessionExpired: Called when a session expires.onSessionCleared: Called when a session is cleared.onSessionEmpty: Called when the app launches and there is no active session.onInitialized: Called when the ZeroXKeyProvider's initialization is complete. An error is carried in the parameters if something goes wrong.
When are multiple sessions useful?
Using multiple sessions can be beneficial when enabling different authentication methods for various operations. For example, you might authenticate a user with OTP for login while using a passkey-based session for signing transactions.