crossmint_flutter 0.3.0
crossmint_flutter: ^0.3.0 copied to clipboard
Flutter SDK for Crossmint wallets, authentication, signers, embedded checkout, and verifiable credentials. Headless-first — you own the UI.
0.3.0 #
- Added
CrossmintIdentityVerification, a WebView widget that renders Crossmint's hosted identity verification step on your own route instead of inside embedded checkout. It reportsonReady,onComplete,onCancelandonError, sizes itself from the hosted page, restricts main-frame navigation to Crossmint hosts while still allowing the provider's capture iframe, and clears its loading overlay with a non-retriable error if the page fails to load. Exported fromcrossmint_flutter_ui.dart. - Added
CrossmintCheckoutController.identityVerificationCredentials, which parses the current order'spayment.preparation.kycand returnsnullwhen the order needs no verification, names an unknown provider, or carries no usable inquiry id. - Added
CrossmintCheckoutConfig.identityVerificationHandling. Setting it toCrossmintIdentityVerificationHandling.externaltells the hosted checkout to stop rendering the verification step, so the app must mountCrossmintIdentityVerificationitself or the buyer cannot finish. The flag is ignored by Crossmint deployments that predate it, which fall back to rendering verification inside checkout. - Verification outcomes and failures are typed as
CrossmintIdentityVerificationStatus,CrossmintIdentityVerificationErrorReasonandCrossmintIdentityVerificationError, each with anunknownfallback, so an unrecognised status still reachesonCompleterather than being dropped. CrossmintPaymentMethodManagementnow reloads when its URL changes. It previously had no update handling, so changingjwt,apiKeyorappearanceon a mounted widget did nothing. Those changes now reload the hosted page. Hosts that rebuild the widget with a new JWT will see a reload where they previously saw none.- The embedded checkout, payment method management and identity verification widgets now share one internal WebView host. Two behaviour changes fall out of it: all three re-show the loading overlay when the hosted page starts a navigation, which only embedded checkout did before, and all three enable the Android file-access and gesture-free media-playback settings that document upload needs, which only embedded checkout did before. These do not grant camera access — see the next entry.
CrossmintEmbeddedCheckoutno longer clears its loading overlay on a sub-resource HTTP error. It previously cleared on any HTTP error, so a failed font or beacon inside the hosted page hid the spinner while the main document was still loading. The overlay now clears only when the failing request is the page itself; theonDiagnosticcallback still fires for every HTTP error, so diagnostics are unchanged.- Runtime camera and microphone permission requests are still not handled:
AndroidWebViewController.setOnPlatformPermissionRequestis not wired up in any widget, so provider live-capture may still be blocked at runtime pending device testing.
0.2.2 #
- Upgraded
app_linksto^7.0.0andflutter_secure_storageto^10.0.0. - These bumps raise upstream platform floors:
flutter_secure_storage10 requires AndroidminSdk23, iOS 12, and Flutter 3.19.0;app_links7 requires iOS 13 and Flutter 3.38.1. The SDK already targets iOS 15. The effective Flutter floor is 3.41.6 (the first Flutter shipping Dart 3.11.4, which thesdk: ^3.11.4constraint requires); theenvironmentblock now declares this explicitly. flutter_secure_storage10 replaces the deprecated Jetpack Crypto backend on Android with new default ciphers and migrates existing entries automatically; it also defaultsresetOnErrortotrue. Hosts that pin customAndroidOptionsshould review the package's v10 migration notes before upgrading.
0.2.1 #
- Solana device signers are no longer rejected client-side. Support is provider-dependent (Swig/Crossmint support device signers, Squads does not) and validated server-side. When a provider rejects a device signer, the runtime transparently falls back to the recovery signer.
- A default device signer is still not eagerly attached to Solana wallet creation, since the backing provider is only known server-side. Explicitly requested device signers now flow through to the server.
- Once a wallet's provider is known not to support device signers, selecting a device signer is rejected before any local key is generated.
- Decoupled
permissionless_passkeys(andpermissionless), dropping the AndroidminSdkfloor from 30 to 24. The experimentalPermissionlessPasskeySignerFactoryis removed; the default passkey factory now yieldswalletUnsupportedPasskeySignerunless a host supplies its own. The supported callback-based passkey path (onCreatePasskey/onSignWithPasskey) is unaffected. - Bumped sub-package floors to
crossmint_core ^0.1.4andcrossmint_wallets ^0.1.3.
0.2.0 #
- Breaking: the minimum iOS deployment target is now 15. This SDK depends
on
crossmint_device_signer0.2.0, whose sharedCrossmintDeviceSignernative pod requires iOS 15. Because the dependency is direct, the floor applies to every consumer's iOS build at CocoaPods resolution time — not only apps that call the device signer at runtime. - Device signer: the hidden device-signer plugin adapter and software
key-storage now delegate to the shared native
CrossmintDeviceSignerpackages — the same native code the React Native SDK uses — for cross-SDK parity. No Dart API change. - Docs: dartdoc / API-reference style pass across the public surface (EVM payer override methods, checkout line-item factories, button properties, inlined enum values, lowercased dartdoc references) for the pub.dev landing page.
- Docs: corrected the hidden signer WebView dartdoc — it is hidden via a 1×1
Opacity(0.01)+IgnorePointerwrapper, not theOffstagewidget — and added a comment explaining why (a zero-area or unpainted WKWebView is reported as not visible on iOS, throttling JS timers and risking WebContent process termination). Mirrors crossmint-sdk React Native PR #1878.
0.1.4 #
- Fix:
addSignernow swaps the runtime wallet's_signerto the recovery signer before approving the pending registration, then restores the previous signer in afinally. Registering a signer is an admin-level op — the backend addresses the pending approval to the wallet's recovery signer, not to whichever signer is currently active. Before this change, callingaddSigner(device2)while signing withdevice1threwsignerApprovalSignerMissingbecause the pending approval'semail:...locator did not match the device signer. Matches the TS SDK (packages/wallets/src/wallets/wallet.ts). Callers that previously worked around this with an explicituseSigner(recovery)beforeaddSignerno longer need to.
0.1.3 #
- Docs: README refresh. Quickstart restructured into four numbered steps
(initialize client → sign user in → mount wallet host → load or create
wallet), with a new "OTP prompt handling" reference subsection covering
both the controller-driven and
onAuthRequiredcallback paths. Documents theCrossmintWalletdata-model vs runtime-wallet split and the four signer-bundled wallet factory helpers (createEvmWalletWithDeviceSigner/…WithNonCustodialSigner/…WithExternalWalletSigner/…WithPasskeySigner). Calls out thatCrossmintWalletControllerConfig.showOtpSignerPromptis inert plumbing today and points atcrossmintDefaultOtpPromptBuilderinstead. Flags theCrossmintEmailSignerConfig()recovery-signer assumption that the signed-in user has an email. - Docs: Embedded Checkout section expanded with a widget-parameter
reference table, a recipient-type guide (
CrossmintCheckoutEmailRecipient/…WalletRecipient/…PhysicalRecipient), a theming subsection coveringCrossmintCheckoutAppearance(variables / rules / fonts), andonOrderCreationFailedalongsideonDiagnostic. Corrects theonOrderUpdatedexample — the callback receivesMap<String, Object?>, so field access usesorder['orderId']rather thanorder.orderId. - Docs: New Platform Setup section covering the Android
AndroidManifest.xmlintent-filter and iOSInfo.plistCFBundleURLTypesentries required for OAuth deep links and theCrossmintAuthCallbackRouterto work. - No code changes. Root package version bumped to 0.1.3 so consumers see the updated README on pub.dev; sub-packages unchanged.
0.1.2 #
- Expanded dartdoc coverage across the public API:
CrossmintClient,CrossmintClientConfig,CrossmintWalletController,CrossmintWalletHost,CrossmintWalletProvider[Config], the EVM / Solana / Stellar runtime wallet classes, everyCrossmintWalletApprovalSignerimplementation (device, non-custodial email/phone, external, API-key, exportable, passkey), theCrossmintAuthClientinterface, and theCrossmintExceptionhierarchy. - Expanded dartdoc coverage across the embedded checkout surface:
CrossmintCheckoutConfig, the recipient classes (Email,Wallet,Physical, +PhysicalAddress), the payment classes (Payment,FiatPayment,CryptoPayment), transaction result variants, diagnostic severity, line-item subclasses, and theCrossmintCheckoutPayer/CrossmintEvmWalletCheckoutPayerbridge. - Added library-level dartdoc headers to every public barrel file under
lib/and the sub-packages underpackages/. - Declared supported platforms (iOS, Android) in
pubspec.yamlso pub.dev no longer reports "unknown platforms" on the package page. - Rewrote
example/README.mdto lead with a minimal copy-paste quickstart (provider + gate + send token + embedded checkout) with the full playground documentation retained below. - Expanded the main
README.mdembedded checkout section with fiat-only, physical-goods, andonDiagnosticsnippets.
0.1.1 #
-
Aligned delegated email/phone signer serialization at wallet creation with the EVM OpenAPI schema. The
signers: [CrossmintEmailSignerConfig(...)]/CrossmintPhoneSignerConfig(...)shapes now serialize to the full{type, email|phone}object instead of the bareemail:/phone:locator string, closing a parity gap withaddSignerand the RN SDK. Forward-compat only — no end-user behavior change in this release: the Crossmint backend does not currently accept email or phone as delegated signer types on EVM. Tracking the backend-side addition separately. See discussion on #21 / #24. -
Fixed device signer approvals on every platform. Three coordinated changes:
- Software fallback (
SoftwareDeviceSignerKeyStorage, used on iOS Simulator, Android emulator, Expo Go, and dev builds without native hardware storage) was UTF-8-encoding the approval message before signing, but the backend delivers a base64-encoded digest. The software signer now base64-decodes the message before signing, matching the RN reference and the native plugins. - Software fallback had no low-S canonicalisation — ERC-4337 / SWIG
verifiers reject high-S signatures to avoid malleability. The
signer now replaces
swithn - swhens > n/2, matching{lowS: true}in the RN signer. - Signature encoding is now decimal (base-10)
r/severywhere (software fallback + native Android Keystore + native iOS Secure Enclave). Software previously emitted hex viaBigInt.toRadixString(16); the native plugins emitted hex with a0xprefix. The wallets API parsesr/sas non-negative decimal BigInt strings and rejects hex with HTTP 400.
Verified end-to-end against staging via the playground's
signers.device.createAndSignMessage/createAndSendTransaction/reuseWithEmptyConfigscenarios. Fixes #23 and the follow-up #31. - Software fallback (
-
Added early diagnostic for non-custodial (email/phone) signers when no
CrossmintWalletHost/CrossmintWalletProvideris mounted in the widget tree.useSigner(CrossmintEmailSignerConfig(...))previously returned cleanly and the first failure surfaced deep inside bridge request polling (StateError: Hidden signer bridge is not connected). It now throwsCrossmintSignerException(code: nonCustodialBridgeHostNotMounted)atuseSignertime with an actionable message. The check is applied to bothwallet.useSigner(...)and theCrossmintWalletController.create{Evm,Solana,Stellar}WalletWithNonCustodialSignerfactory helpers. Fixes #22. -
Added
CrossmintWalletHostandHiddenSignerBridgeHosttopackage:crossmint_flutter/crossmint_flutter_controllers.dart. Both widgets arewidgets.dart-only and satisfy the headless-first import invariant, so apps using their own design system no longer need to pull in the Material UI barrel to host the hidden signer bridge. Thecrossmint_flutter_ui.dartre-exports continue to work unchanged. README Quickstart updated with the wrapping example and the non-custodial-signer host requirement. -
Added
CrossmintErrorCode.nonCustodialBridgeHostNotMountedtocrossmint_core. -
Added a public
HiddenSignerBridgeRuntimeController.hasEverBeenHostedgetter that flipstruethe first time aHiddenSignerBridgeHostattaches a port and staystrueon detach — the signal that backs the new missing-host diagnostic. -
Fixed
useSigner(CrossmintDeviceSignerConfig())against a wallet that already has a device signer registered. The runtime used to unconditionally register a second device signer, which the backend treats as adding a new delegated signer gated behind an admin/recovery-OTP approval. It now inspects the wallet's delegated signers and reuses the registered device signer when exactly one exists and its key is present locally, avoiding the duplicate-signer OTP round-trip. Falls through to fresh registration when the wallet has no device signer, when multiple device signers create ambiguity, or when the referenced key is not available in local storage (reinstall / wiped-storage case). Refreshes the wallet snapshot once when the local snapshot lacksconfig.delegatedSigners(trimmed-response case). Fixes #27. -
Fixed
useSigner(CrossmintDeviceSignerConfig(publicKeyX, publicKeyY))— previously silently dropped the caller's public-key material and registered a brand-new key.publicKeyX/publicKeyYand the device locator encode the same material (device:<base64(0x04 ‖ x ‖ y)>), so the canonical locator is now derived from the supplied public key, verified against the wallet's registered device signers, and activated. ThrowsCrossmintSignerExceptionon mismatch or when only one ofpublicKeyX/publicKeyYis provided. Refreshes the wallet snapshot once on any local miss (addSignerdoes not keepwallet.raw.config.delegatedSignersin sync, so a stale snapshot may list a different device signer whilegetWallet(...)already includes the requested one). Fixes #29.
0.1.0 #
- Fixed
CrossmintNonCustodialSignerdeduplicating concurrentensureAuthenticated()callers so a single in-flight auth attempt no longer surfaces twoonAuthRequiredprompts for the same auth event. - Fixed
CrossmintSignerBridgeClientcross-talk between concurrent calls that share a response event (e.g. tworequest:signin flight). Requests are now serialized per response event so unrelated event pairs (e.g. aget-statuswhile asignis pending) still run in parallel. Source-compatible source break:CrossmintSignerBridgeClientis no longerconst-constructible (no in-tree callers usedconst, but downstream code that did will need to drop theconstkeyword). - Fixed
CrossmintWalletProviderLifecycle.disposeDependencies()to keep disposing the auth router and client when the wallet controller's disposer throws. Failures are reported viaFlutterErrorinstead of skipping the remaining cleanup. - Added
CrossmintDeserializationExceptionand typed parser helpers (requireString,optionalString,requireInt,requireBool,requireMap,requireMapList) incrossmint_core.CrossmintUser.fromJsonis migrated as the first caller; remaining model fromJson sites will follow in a separate PR. - Added
CrossmintErrorCodeenum tocrossmint_core— typed, machine-readable error codes for programmaticswitch/caseexception handling. All exception subclasses now carry an optionalcodefield (defaults tounknown). Codes cover auth, wallet, signer, credential, order, and token domains. - Added
CrossmintPollingConfigandcrossmintPoll()utility tocrossmint_core— exponential backoff with jitter replaces the previous flat 1-second polling loops in signature and transaction confirmation flows. Default: 1 s initial, 2x growth, 8 s cap, 60 attempts, random jitter in [50 %, 100 %) range. - Added ADR-001 documenting the decision to keep hand-written
fromJson()/toJson()methods rather than adoptingfreezedorjson_serializablecodegen, with clear re-evaluation triggers. - Added
CrossmintChainenum tocrossmint_core— typed, environment-aware chain validation covering all 24 mainnet chains, 20 testnet chains, Solana, and Stellar. IncludesfromApiValue()/tryFromApiValue()for safe parsing andvalidateForEnvironment()for automatic mainnet→testnet conversion in non-production environments. - Added
CrossmintApiKeySigner— a backend-only signer matching the official TypeScript SDK's API key signer. All signing is handled server-side; callingsignMessage()orsignTransaction()locally throws a descriptive error. - Added
CrossmintPasskeySignerConfigmodel andcrossmintGuardAgainstPasskeySigners()guard. Passkey/WebAuthn signers are blocked in Flutter (matching the official React Native SDK), with a clear error message guiding users to alternative signer types. - Added
CrossmintApiKeySignerConfigandCrossmintPasskeySignerConfigto theCrossmintSignerConfigsealed hierarchy incrossmint_wallets. - Added
fromJson()/toJson()factory methods to all core model classes:CrossmintWallet,CrossmintSignerConfig(polymorphic dispatch by type),CrossmintWalletSigner,CrossmintTransactionRecord,CrossmintSignatureRecord,CrossmintWalletBalanceSnapshot,CrossmintNftRecord,CrossmintWalletNftPage,CrossmintUser,CrossmintOrderRecord,CrossmintTokenAvailabilityRecord, and others. - Replaced hand-rolled base58 encoder/decoder (~65 lines) with the published
bs58package. The_decodeBase58()and_encodeBase58()helpers incrossmint_non_custodial_signer.dartnow delegate tobase58.decode()andbase58.encode(). - Upgraded chain-type detection helpers (
crossmintIsSolanaChain(),crossmintIsStellarChain(),_isSolanaChain()) to prefer enum-based matching viaCrossmintChain.tryFromApiValue()with string fallback for backward compatibility. - Validated iOS real-device integration tests on physical iPhone via Wi-Fi debugging, closing the last pending validation item. Hidden signer bridge and device signer plugin (Secure Enclave) both passed.
- Documented the Xcode 26.4 / Flutter #184254 workaround (Wi-Fi debugging) for future iOS physical-device test runs.
- Fixed headless order parsing to handle the live
order.orderIdresponse shape returned byPOST /api/2022-06-09/orders. - Fixed
scripts/dart_defines_from_env.shso JSON-valued Dart defines work with direct command substitution in the documentedflutter run/test $(...)flow. - Added a direct staging probe for orders / Worldstore so client-key vs server-key boundaries are documented with live evidence.
- Added release-readiness docs for gate disposition, Android/iOS evidence, and the release-candidate checklist.
- Clarified that externally gated scopes remain non-blocking amber when the Flutter SDK contract is already proven correct.
- Added example-workspace package overrides so
flutter driveand other example-side validation paths resolve the local package family consistently. - Switched the package family to publish-ready hosted dependency manifests while
keeping local monorepo development on
pubspec_overrides.yaml. - Added consumer-facing installation guidance for
crossmint_flutterand cleaned up template metadata in the native device-signer plugin package.
0.0.1 #
- Initial internal Flutter port workspace release.
- Added the core Crossmint Flutter client surface for auth, wallets, credentials, orders, tokens, and users.
- Added the device signer plugin, hidden signer bridge, example app, and test harnesses.
- Known gaps at this release stage: iOS real-device automation and staging scopes gated by project/backend configuration.