Mobile & Web SDKs · v1.2.0

Flutter SDK

One Dart package, two native bridges. Material widgets shipped in the box. Phone OTP, WhatsApp OTP, marketing attribution — all driving the same backend as the rest of QuickAuth.

Requirements

  • Flutter 3.16+
  • Dart SDK ≥ 3.0.0
  • iOS 12.0+, Android minSdk 21

Install

flutter pub add quickauth_flutter

Initialize

main.dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:quickauth_flutter/quickauth_flutter.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await QuickAuth.init(
    // In-app-safe. Issue it in the dashboard; it is scoped to OTP
    // initiate/verify, app-lockable and rate-limited, so it ships in the binary.
    publishableKey: 'pk_live_...',
    // Single typed event stream — switch on the sealed AuthEvent subtypes.
    onAuthEvent: (event) {
      switch (event) {
        case OtpSentEvent():     showOtpInput();
        case OtpAutoReadEvent(): prefillInput(event.code);
        case VerifiedEvent():    finishLogin(event.requestId);
        case OtpFailedEvent():   showError(event.message);
        case AuthErrorEvent():   showError(event.message);
      }
    },
  );

  runApp(const MyApp());
}

Publishable key, or a session token

publishableKey needs no backend. It is accepted only on /v1/sdk/auth/**, scoped to OTP initiate and verify, rate-limited, and lockable to your registered app — so it is safe in a shipped binary in a way a client secret is not.

onTokenExpiry still works if you already mint session tokens server-side. Pass one or the other; passing both throws.

Consent

dart
await QuickAuth.consent.set(true);
final granted = QuickAuth.consent.get();   // synchronous — returns bool, not Future

Headless flow

Two methods. initiate kicks off the auth attempt; submitOtp hands back the user-typed code. All outcomes arrive on the onAuthEvent handler — including the OneTap silent re-auth path, where the backend emits VerifiedEvent directly without sending an SMS.

dart
// Step 1 — kick off the attempt. Events arrive via onAuthEvent.
await QuickAuth.auth.initiate(
  phone:   '+919876543210',
  channel: OtpChannel.auto,
);
// → onAuthEvent fires with one of:
//     OtpSentEvent(sessionId, channel, expiresIn)
//     VerifiedEvent(requestId, message)            // OneTap fired
//     AuthErrorEvent(code, message)

// Step 2 — when OtpSentEvent fires and the user types a code:
await QuickAuth.auth.submitOtp('123456');
// → onAuthEvent fires with VerifiedEvent / OtpFailedEvent / AuthErrorEvent

// Resend takes no arguments — it repeats the attempt already in flight, carrying its
// channel and autoSubmit. Passing a number again would start a second transaction and
// leave the user holding two codes, only one of which works.
await QuickAuth.auth.resendOtp();

// On user-initiated logout, drop the OneTap trust token:
await QuickAuth.auth.reset(forgetDevice: true);

Pre-built widgets

import 'package:quickauth_flutter/quickauth_flutter.dart';

QuickAuthLoginButton(
  phone:    '+919876543210',
  onSuccess: (jwt) => saveJwt(jwt),
  onError:   (err) => debugPrint(err.toString()),
)

Auto-fill is native

iOS auto-fill is wired via UITextContentType.oneTimeCode in the native bridge. Android uses SMS Retriever — no permission prompt — and the bridge pushes the code into the Flutter widget over a platform stream.

WhatsApp auto-read 1.2.0

WhatsApp does not deliver zero-tap and one-tap codes over SMS. It broadcasts them to the app named in the template's supported_apps, which is why SMS Retriever never saw them. Android only — on iOS and WhatsApp Web there is no app to bind to, and those templates use copy-code instead.

dart
// An auto-read code verifies itself, so the integration is initiate-and-listen.
await QuickAuth.auth.initiate(
  phone:      '+919876543210',
  channel:    OtpChannel.whatsapp,
  autoSubmit: true,
);
// → OtpAutoReadEvent(code) then VerifiedEvent — no submitOtp() call needed.

// observeOTP() covers BOTH channels, so a merchant on OtpChannel.auto gets the
// same behaviour whichever way the code arrives.
QuickAuth.auth.observeOTP().listen(prefillInput);

autoSubmit is off by default, deliberately

An app that already submits from its own observeOTP callback would otherwise submit twice, and the second attempt fails against a code the server has consumed — an error arriving after a success. Turn it on only if you are not submitting yourself.

WhatsApp OTP

dart
// Opens wa.me with a prefilled message. Returns false if WhatsApp could not be opened.
final opened = await QuickAuth.whatsapp.open(
  businessNumber:   '+919574980048',
  prefilledMessage: 'Hi, I want to log in.',
);

// Handle the return in your route observer or app_links callback:
await QuickAuth.attribution.captureLaunch(launchUri: uri);

Attribution

dart
// On cold launch (inside initState of your root widget):
await QuickAuth.attribution.captureLaunch();

// Track a conversion later:
await QuickAuth.attribution.trackConversion(
  event:    'signup',
  value:    0,
  currency: 'INR',
);

API reference

SymbolDescription
QuickAuth.init(...)Async init in main(). Takes publishableKey or onTokenExpiry.
QuickAuth.consent.set / getConsent gate. get() is synchronous.
.auth.initiate(phone:channel:autoSubmit:)Begin an auth attempt. Emits OtpSentEvent or VerifiedEvent.
.auth.resendOtp()Repeat the attempt in flight. No arguments. Throws StateError if none.
.auth.submitOtp(code)Submit the user-typed code. Emits VerifiedEvent or OtpFailedEvent.
.auth.reset(forgetDevice:)Reset the state machine. Pass forgetDevice: true on logout.
.auth.observeOTP()Stream<String> of inbound OTPs — SMS and WhatsApp both.
QuickAuth.whatsapp.open(...)Open wa.me with a prefilled message. Returns false if it could not launch.
QuickAuthLoginButtonMaterial login widget.
QuickAuthOtpFieldMaterial OTP input widget.
QuickAuth.attribution.captureLaunch / trackConversionAttribution APIs.

Source & changelog

Source: github.com/quickauthin/quickauth-sdk-flutter. Package: quickauth_flutter on pub.dev.