Flutter quickstart

Last updated  Sep 9, 2026

One plugin wraps the native iOS and Android SDKs. Add the git dependency, do the two-line platform setup, set credentials, and present.

Install the SDK

The plugin is consumed as a git dependency pinned to a release tag.

pubspec.yaml
dependencies:  voqal_flutter:    git:      url: https://github.com/VoqalAI/voqal-flutter.git      ref: "2.1.0"

Platform setup

Android needs the Voqal Maven repository (same as the native SDK) and a FlutterFragmentActivity host. iOS needs a 16.0 platform floor and a microphone usage description.

android/app/src/main/kotlin/MainActivity.kt
import io.flutter.embedding.android.FlutterFragmentActivity// Must extend FlutterFragmentActivity (a ComponentActivity), NOT FlutterActivity.class MainActivity : FlutterFragmentActivity()
ios/Podfile + Info.plist
# ios/Podfileplatform :ios, '16.0'<!-- ios/Runner/Info.plist --><key>NSMicrophoneUsageDescription</key><string>Voqal uses the microphone for voice conversations.</string>

Configure and set credentials

Call setuponce at app start, then hand the SDK your end user’s auth token with setCredentials — refresh it whenever it rotates.

main.dart
import 'package:voqal_flutter/voqal_flutter.dart';final voqal = Voqal();await voqal.setup(const VoqalConfig(  apiKey: 'pk_live_…',              // your Voqal API key (required, public)  requestId: 'prod-yourtenant',    // "prod-" / "stg-" selects the environment  theme: VoqalTheme(accent: '#2d5bff', appearance: VoqalAppearance.auto),));// Credentials are set separately and can be refreshed at any time.await voqal.setCredentials(  yourAuthToken,  metadataJson: '{"country_code":"EGY","user_id":"123"}',);await voqal.prewarm();   // optional: warm the connection so the first turn is instant
prewarm opens the engine connection in the background so the assistant answers instantly the first time it opens.

Forwarding headers

New in 2.0.0. Give setup a forwardedHeaders provider to send your own headers to your backend on every turn. Each entry reaches the engine namespaced as X-Voqal-Forward-<name>; the engine strips the prefix and forwards the header verbatim to your tenant’s backend. The function is read live on every turn, so a rotating value — a short-lived Authorization, for example — always sends its current value.

main.dart
await voqal.setup(VoqalConfig(  apiKey: 'pk_live_…',  requestId: 'prod-yourtenant',  // Evaluated fresh on every turn — a rotating token always sends its current value.  forwardedHeaders: () => {    'Authorization': 'Bearer $accessToken',    'X-Tenant-Id': tenantId,  },));

This is a different path from metadata. setCredentials(token, metadataJson: …) sends X-Client-Metadata— Voqal’s own context (country, user id) that the engine reads to route and personalize. Forwarded headers are passed straight through to your backend and never interpreted by Voqal.

Reserved control headers (X-Token, X-Voqal-Key, X-Request-Id, X-Client-Metadata, Content-Type, Accept) are dropped if returned — they can never override the SDK’s own headers. Authorization is forwarded. At most 32 headers are sent.

Redirect & checkout

When the agent surfaces a confirm widget — say, a completed cart — you often want to hand the user back to your own screen to finish. Set actionButtonEnabled: true to render an accent button beside the voice wave, then handle onActionButtonTapped to navigate. The callback carries no payload: the SDK has already dismissed itself, and your app decides where to go.

The button is off by default. Leave actionButtonEnabled out (or set it to false) and nothing is drawn beside the voice wave — right for a product with no checkout, such as a booking or support assistant.

home_screen.dart
// 1. Turn the action button on in config.await voqal.setup(VoqalConfig(  apiKey: 'pk_live_…',  actionButtonEnabled: true,   // accent button beside the voice wave));// 2. Handle the tap — no payload; you decide where to go.voqal.onActionButtonTapped = () {  Navigator.of(context).push(    MaterialPageRoute(builder: (_) => const CheckoutPage()),  );};
Money-movement stays inside the engine’s confirm widget — the action button is purely a redirect into your app’s own flow (checkout, an order page, a receipt).

Present the assistant

Open the assistant from any widget.

home_screen.dart
ElevatedButton(  onPressed: () => voqal.present(),  child: const Text('Talk to Voqal'),)
  • Voice, transcription, widgets, and confirmations are handled inside the sheet — your app ships no assistant UI.
  • Theming, presentation style, header branding, and the action button share one model across platforms — see Configuration.

Your own button

There is no Voqal launcher widget in Flutter. Any tappable thing you already have — a FloatingActionButton, an icon in the app bar, a row in a menu, a deep link — calls voqal.present(). The assistant opens over your screen and manages its own navigation until the user dismisses it.

home_screen.dart
// A floating action button — the plugin ships no launcher widget; this is yours.Scaffold(  floatingActionButton: FloatingActionButton.extended(    onPressed: () => voqal.present(),    icon: const Icon(Icons.mic),    label: const Text('Ask'),  ),  body: …,)

By default it comes up as a near-full sheet the user can swipe down. For an edge-to-edge screen that closes only from its own button, set presentationStyle at setup — it is not a per-call option.

main.dart
// Setup-time setting: apply before present().await voqal.setup(VoqalConfig(  apiKey: 'pk_live_…',  presentationStyle: VoqalPresentationStyle.fullScreen, // default: .sheet));
Call voqal.prewarm() once after setCredentials so the connection is already open when the user taps — the first turn lands noticeably faster.
© 2026 VoqalVoqal SDK & engine documentation