engagive_flutter_sdk

Native Flutter SDK for Engagive AppChat visitor messaging. Embed in-app support chat in your Flutter app without a WebView.

Version: 1.0.0 · pub.dev · Changelog

Your Flutter app  →  Your backend (mint identify_token)  →  Engagive API  →  Agents / AI

This guide is for developers integrating the package from pub.dev.


Table of contents

  1. What the SDK does
  2. Prerequisites (Engagive portal)
  3. Install from pub.dev
  4. Your backend responsibility
  5. Common setup (every app)
  6. Use cases
  7. API reference
  8. Message types
  9. Security checklist
  10. Example application setup
  11. Troubleshooting
  12. License

What the SDK does

Area Capability
UI EngagiveChatScreen, Engagive.openChat, theme from portal Mobile SDK → Flutter
Identity Trusted, anonymous guest, guest→trusted upgrade
Messaging Text, image, video, audio, document, location, interactive, template, sticker
Composer Attach menu, camera/gallery, voice note, captions, location
Thread Replies, reactions, ticks, date separators, markdown, retry, offline banner
Realtime WebSocket messages, status, reactions, AI typing/stream
Session Secure persistence + restore
Push Optional FCM for identified users (Android + iOS)
Recovery Portal recovery rules via EngagiveRecoveryRequirements

Identity is host-driven (what you pass to identify APIs). It is not gated by the website widget’s portal identity_mode.


Prerequisites (Engagive portal)

Before coding:

  1. Active AppChat channel for your business.
  2. Portal → Mobile SDK → Flutter: enable in-app chat / SDK access.
  3. Copy widget public key (safe in the app).
  4. Keep widget secret key on your server only (never in Flutter / pub / git).
  5. Note your Engagive API base URL (e.g. https://api.engagive.io or your brand host).
  6. Add your app’s package / bundle id to the Flutter app identifier allowlist (e.g. com.yourcompany.app).
  7. (Optional push) Configure Firebase in the portal and ship client config in the host app.

Install from pub.dev

dependencies:
  engagive_flutter_sdk: ^1.0.0
flutter pub get

Requires Flutter 3.44.2 and Dart 3.12.2.

import 'package:engagive_flutter_sdk/engagive_flutter_sdk.dart';

Platform notes:

  • Android / iOS are the primary targets (chat + optional FCM).
  • Add platform permissions as needed for camera, mic, location, notifications (same as any chat app).
  • For push: apply Google Services (Android) and add GoogleService-Info.plist + Push capability (iOS).

Your backend responsibility

The Flutter app must never hold widget_secret_key.

Your backend should:

  1. Authenticate the end user (or authorize a guest bootstrap).
  2. Mint a short-lived HS256 JWT (identify_token, typically ~5 minutes).
  3. Return that token to the app over your authenticated API.
  4. Use the correct identity_kind for the flow:
Flow identity_kind What the token must bind
Trusted trusted Your logged-in user id
Anonymous anonymous The guest / anonymous id
Upgrade upgrade Trusted user id and link to the previous guest id

The app then calls Engagive.identifyUser / identifyAnonymous / upgradeUser with that token. Signing details live in Engagive portal docs / your integration guide — keep secrets server-side.


Common setup (every app)

Step 1 — Init once at startup

final rootNavigatorKey = GlobalKey<NavigatorState>();

await Engagive.init(
  EngagiveConfig(
    publicKey: yourWidgetPublicKey,      // from your config / remote config
    apiBaseUrl: 'https://api.engagive.io',
    appIdentifier: 'com.yourcompany.app', // must match portal allowlist
    navigatorKey: rootNavigatorKey,       // needed for notification → chat
    restoreSessionOnInit: true,
    connectRealtimeOnIdentify: true,
    enableNotifications: true,
    enablePushNotifications: false,       // set true when FCM is ready
  ),
);

Wire navigatorKey into your MaterialApp / CupertinoApp.

Step 2 — Identify before opening chat

Call one of the identify APIs (see use cases). Check:

Engagive.isInitialized; // true after init
Engagive.isIdentified;  // true after successful identify / restore

Step 3 — Show chat

Either push a route or embed a screen (use cases D and E).


Use cases

Use case A — Trusted login only

When: Support is only for logged-in users.

Steps

  1. User signs into your app.
  2. Your backend returns identify_token for that user (identity_kind: trusted).
  3. Call Engagive.identifyUser with EngagiveUser(id: …).
  4. Open chat (openChat or EngagiveChatScreen).
  5. On logout, call Engagive.logout().
Future<void> onHostLogin(AppUser user) async {
  final token = await yourApi.fetchEngagiveIdentifyToken(user.id);

  await Engagive.identifyUser(
    EngagiveUser(
      id: user.id,
      name: user.name,
      email: user.email,
      phoneNumber: user.phone,
      countryCode: user.countryCode, // required if phone is set
    ),
    identifyToken: token,
  );
}

// Later
await Engagive.openChat(context);

Use case B — Anonymous guest chat

When: Users can chat before signing in.

Steps

  1. Create or load a stable guest id (SDK can persist one for you).
  2. Your backend mints an anonymous identify_token for that guest id.
  3. Call Engagive.identifyAnonymous (or identifyUser with only anonymousUserId).
  4. Open chat.
  5. Keep the same guest id across restarts so history continues.
Future<void> startGuestChat() async {
  final guestId = await Engagive.getOrCreateAnonymousUserId();
  final token = await yourApi.fetchEngagiveAnonymousToken(guestId);

  await Engagive.identifyAnonymous(
    EngagiveUser(
      anonymousUserId: guestId,
      name: optionalName,
      email: optionalEmail,
      phoneNumber: optionalPhone,
      countryCode: optionalCountryCode,
    ),
    identifyToken: token,
  );

  await Engagive.openChat(context);
}

Helpers:

Engagive.storedAnonymousUserId;
await Engagive.getOrCreateAnonymousUserId();

Use case C — Guest then upgrade after login

When: Guest chats first; after login you want the same thread under the trusted user.

Steps

  1. Complete Use case B and keep the guest id.
  2. User logs into your app.
  3. Backend mints an upgrade token linking trusted id ↔ that guest id.
  4. Call Engagive.upgradeUser (or identifyUser with both id and anonymousUserId).
  5. Continue using the same chat UI — history merges.
// Before login — guest (Use case B)
final guestId = await Engagive.getOrCreateAnonymousUserId();
await Engagive.identifyAnonymous(
  EngagiveUser(anonymousUserId: guestId),
  identifyToken: anonToken,
);

// After your login
final upgradeToken = await yourApi.fetchEngagiveUpgradeToken(
  userId: user.id,
  anonymousUserId: Engagive.storedAnonymousUserId ?? guestId,
);

await Engagive.upgradeUser(
  EngagiveUser(
    id: user.id,
    anonymousUserId: Engagive.storedAnonymousUserId ?? guestId,
    name: user.name,
    email: user.email,
  ),
  identifyToken: upgradeToken,
);

Equivalent:

await Engagive.identifyUser(
  EngagiveUser(id: user.id, anonymousUserId: guestId),
  identifyToken: upgradeToken,
);

Use case D — Open chat as a pushed route

When: A “Contact support” button should push a full-screen chat (back button pops).

ElevatedButton(
  onPressed: () => Engagive.openChat(context),
  child: const Text('Contact support'),
);

// Optional chrome
await Engagive.openChat(
  context,
  showBackButton: true,
  showAppBarMenu: true,
  appBarMenuIconAsset: 'assets/icons/chat-menu.png',
);
Parameter Default Purpose
showBackButton true Shows back; pops the route
showAppBarMenu true Restart-conversation menu
appBarMenuIcon null Custom menu widget
appBarMenuIconAsset null Asset for menu icon
visible inferred Override when host controls visibility / mark-read

Use case E — Embed chat in a tab / panel

When: Chat is a bottom-nav tab or side panel (host owns navigation).

BottomNavigationBar(
  // …
);

// Tab body
const EngagiveChatScreen(
  showBackButton: false, // host handles back / tab switch
  showAppBarMenu: true,
);

If the chat tab can be covered or backgrounded, pass visible: so mark-read stays correct.

EngagiveChatBubble is deprecated — build your own launcher and call openChat or embed EngagiveChatScreen.


Use case F — Session restore on cold start

When: Returning users should not re-identify every launch (within session validity).

Steps

  1. Keep restoreSessionOnInit: true (default).
  2. Call Engagive.init on startup.
  3. If Engagive.isIdentified is true, you may open chat directly.
  4. If restore fails (expired / invalid), fetch a fresh identify_token and identify again.
await Engagive.init(config);

if (!Engagive.isIdentified) {
  // Your login / guest bootstrap → identify…
}

Use case G — Logout

await Engagive.logout();                     // clear Engagive session
await Engagive.logout(clearAnonymous: true); // also wipe persisted guest id

Use clearAnonymous: true when the host account fully signs out and you do not want the next visitor to resume the previous guest thread.


Use case H — Chat history recovery

Portal website-widget recovery rules (require_email / require_phone) apply to SDK sessions as recovery_mode.

Steps

  1. Optionally prefetch settings before your profile form.
  2. Ensure EngagiveUser includes the fields recovery requires.
  3. Identify as usual.
  4. Handle recovery errors (e.g. prompt trusted login).
final settings = await Engagive.prefetchSettings(
  identifyToken: token,
  externalUserId: user.id,
);
final recovery = settings.recoveryRequirements;

if (recovery.canAttemptRecovery &&
    !recovery.credentialsCompleteForRecovery(
      email: user.email,
      phoneNumber: user.phone,
    )) {
  // Ask user for missing email / phone before identify
}

try {
  await Engagive.identifyUser(
    EngagiveUser(id: user.id, email: user.email, /* … */),
    identifyToken: token,
  );
} on EngagiveException catch (e) {
  if (e.errorCode == EngagiveErrorCode.userIdRequiredForRecovery) {
    // Prompt: sign in with app account to continue
  }
  rethrow;
}

Also available after identify: Engagive.settings?.recoveryRequirements.


Use case I — Visitor push (FCM)

When: Identified users should get agent/AI replies while the app is backgrounded or killed.

Steps

  1. Configure Firebase in Engagive portal for the business.
  2. Ship google-services.json (Android) / GoogleService-Info.plist (iOS) + APNs key in Firebase.
  3. Enable push in EngagiveConfig.
  4. Identify a trusted or upgrade user (guests are not push-eligible).
  5. Provide navigatorKey so taps can open chat.
  6. QA with Engagive.pushDiagnostics.
await Engagive.init(
  EngagiveConfig(
    publicKey: yourWidgetPublicKey,
    apiBaseUrl: yourApiBaseUrl,
    appIdentifier: 'com.yourcompany.app',
    navigatorKey: rootNavigatorKey,
    enablePushNotifications: true,
    firebaseProjectId: optionalFirebaseProjectId,
    // pushIntegration: MyCustomPushIntegration(), // optional
  ),
);

await Engagive.identifyUser(
  EngagiveUser(id: user.id),
  identifyToken: token,
);

debugPrint('${Engagive.pushDiagnostics}');

Use case J — Unread badge

ValueListenableBuilder<int>(
  valueListenable: Engagive.unreadCount,
  builder: (context, count, _) {
    return Badge(
      isLabelVisible: count > 0,
      label: Text('$count'),
      child: IconButton(
        icon: const Icon(Icons.chat),
        onPressed: () => Engagive.openChat(context),
      ),
    );
  },
);

Realtime streams (optional host wiring):

Engagive.realtimeEvents;
Engagive.realtimeConnectionStates;
Engagive.realtimeConnectionState;

API reference

EngagiveConfig

Field Required Default Notes
publicKey yes — Widget public key
apiBaseUrl yes — Origin, no trailing slash
wsBaseUrl no from API URL WebSocket override
appIdentifier recommended — Portal allowlist
restoreSessionOnInit no true Secure session restore
connectRealtimeOnIdentify no true Connect /ws after identify
enableNotifications no true Local notifications from WS
enablePushNotifications no true FCM path for identified users
firebaseProjectId no — Sent on token register
navigatorKey no — Notification → open chat
pushIntegration no built-in FCM Custom EngagivePushIntegration

EngagiveUser

Field Notes
id Trusted logged-in user id
anonymousUserId Host-owned guest id
name / email Optional profile
phoneNumber Optional; if set, countryCode is required
countryCode E.164 prefix, e.g. +92

Identity methods

Method Typical EngagiveUser
Engagive.identifyUser id and/or anonymousUserId
Engagive.identifyAnonymous anonymousUserId
Engagive.upgradeUser id + anonymousUserId
Engagive.logout optional clearAnonymous
Engagive.getOrCreateAnonymousUserId persists guest id
Engagive.prefetchSettings recovery / theme before identify

State getters

Engagive.isInitialized;
Engagive.isIdentified;
Engagive.config;
Engagive.session;
Engagive.user;
Engagive.settings;
Engagive.storedAnonymousUserId;
Engagive.unreadCount;
Engagive.pushDiagnostics;
Engagive.pushIntegration;

UI

  • Engagive.openChat(context, …)
  • EngagiveChatScreen(…)
  • EngagiveChatController (advanced)

Push / media helpers

  • EngagivePushIntegration, EngagivePushPayload, EngagivePushDiagnostics
  • EngagiveMediaPicker, EngagiveMediaPolicy, EngagiveMediaUrl
  • EngagiveTheme, EngagiveRecoveryRequirements, EngagiveSession

Message types

Receive: text, image, video, audio, document, location, interactive, template, sticker, contacts

Send: text, image, video, audio, document, location, interactive reply, template quick-reply

Default media limits: 10 MB (most types), 25 MB (video).


Security checklist

  • Only public key in the Flutter app / remote config
  • Secret key only on your backend
  • identify_token minted server-side after your auth
  • appIdentifier matches portal allowlist
  • No secrets in git, CI logs, or pub package source
  • Example app local JWT signing is demo-only — do not copy to production

Example application setup

The package ships a full host demo under example/. It mirrors a real product: login, guest, upgrade, and an embedded chat tab.

1. Get credentials

From Engagive portal → Mobile SDK / widget:

  • Widget public key
  • Widget secret (for the example’s local demo signer only)
  • API base URL
  • App id allowlisted (example default: com.engagivestudio.demo)

2. Fetch and run

# After: flutter pub get in the package (or clone from GitHub / pub cache)
cd example
flutter pub get

flutter run \
  --dart-define=ENGAGIVE_PUBLIC_KEY=YOUR_PUBLIC_KEY \
  --dart-define=ENGAGIVE_WIDGET_SECRET_KEY=YOUR_SECRET_KEY \
  --dart-define=ENGAGIVE_API_BASE_URL=https://api.engagivestudio.com \
  --dart-define=ENGAGIVE_APP_IDENTIFIER=com.engagivestudio.demo
Dart-define Required Default
ENGAGIVE_PUBLIC_KEY yes empty
ENGAGIVE_WIDGET_SECRET_KEY for local JWT sign empty
ENGAGIVE_API_BASE_URL no https://api.engagivestudio.com
ENGAGIVE_APP_IDENTIFIER no com.engagivestudio.demo
ENGAGIVE_ENABLE_PUSH no false
ENGAGIVE_FIREBASE_PROJECT_ID no empty
ENGAGIVE_IDENTIFY_TTL_SECONDS no 300

Without a secret, use a portal Developer test token in the example Settings screen (paste identify token).

3. Try each flow in the example

Flow What to do in the UI
Trusted Enter a user id (e.g. studio_demo_customer_1) → Continue → open Chat tab
Guest Skip — continue as guest → Chat
Upgrade Start as guest → chat → Sign in with a user id → history merges
Embedded UI Chat tab uses EngagiveChatScreen(showBackButton: false)

4. Debug APK (optional)

cd example
flutter build apk --debug \
  --dart-define=ENGAGIVE_PUBLIC_KEY=YOUR_PUBLIC_KEY \
  --dart-define=ENGAGIVE_WIDGET_SECRET_KEY=YOUR_SECRET_KEY \
  --dart-define=ENGAGIVE_APP_IDENTIFIER=com.engagivestudio.demo

APK: example/build/app/outputs/flutter-apk/app-debug.apk

5. Map example → production

Example Production
Local JWT signer + secret dart-define Your backend endpoint mints identify_token
Login screen user id field Your real auth user id
Settings paste-token Not needed if backend always supplies tokens
Studio API URL / app id Your brand API URL + production package name

More detail: example/README.md.


Troubleshooting

Symptom Check
Identify fails / 401 Token expired? Wrong secret? appIdentifier allowlisted?
“Public key required” in example Pass ENGAGIVE_PUBLIC_KEY
Chat empty / not loading Engagive.isIdentified? Network / API base URL?
Guest history lost after reinstall New guest id — use recovery email/phone or sign in (upgrade)
Push never arrives Trusted/upgrade only; Firebase configs; enablePushNotifications; pushDiagnostics
Mark-read wrong in tabs Pass visible: on EngagiveChatScreen when tab is hidden
WS drops with auth error Re-fetch identify_token and identify again
} on EngagiveException catch (e) {
  debugPrint('${e.errorCode} ${e.message}');
}

License

MIT — see LICENSE.

Libraries

engagive_flutter
Legacy export path — prefer package:engagive_flutter_sdk/engagive_flutter_sdk.dart.
engagive_flutter_sdk
Engagive Flutter SDK — native in-app support chat for AppChat visitors.