termii_resolve_livechat 0.2.1 copy "termii_resolve_livechat: ^0.2.1" to clipboard
termii_resolve_livechat: ^0.2.1 copied to clipboard

Termii Resolve Live Chat SDK for Flutter. Add Resolve in-app customer support to any Flutter app in a few lines, with user identification and unread-count events.

Termii Resolve Live Chat Flutter SDK #

Termii Resolve Live Chat SDK for Flutter. Add Resolve in-app customer support to any Flutter app in a few lines.

All chat UI, messaging, and real-time transport are hosted by Resolve, so fixes and new features reach your users without an SDK update.

Features #

  • 💬 Real-time messaging over WebSocket
  • 📱 iOS and Android
  • 👤 Anonymous and identified users
  • 📎 File attachments (images, documents, videos) and emoji picker
  • 🎨 Theming and branding from the Resolve page in your Termii dashboard
  • 🔔 Unread counts while the chat is closed
  • ⚡ Warm start — reopening the chat is instant

Quick Start #

dependencies:
  termii_resolve_livechat: ^0.2.0
import 'package:termii_resolve_livechat/termii_resolve_livechat.dart';

await ResolveLiveChat.open(context, widgetId: 'YOUR_WIDGET_ID');

That pushes a full-screen chat route and pops it when the user taps the chat's close button. Your widgetId comes from the Resolve page in your Termii dashboard.

Platform Setup #

Android — minimum SDK 21. Add the INTERNET permission to android/app/src/main/AndroidManifest.xml (debug builds get it from Flutter's debug manifest, release builds need it here):

<uses-permission android:name="android.permission.INTERNET" />

iOS — nothing to add; the chat page is served over HTTPS.

Identifying Users #

Anonymous visitors can chat immediately. Pass a ResolveUser to link the conversation to a profile:

await ResolveLiveChat.open(
  context,
  widgetId: 'YOUR_WIDGET_ID',
  user: ResolveUser(
    email: 'jane@example.com', // required
    name: 'Jane Doe',
    phone: '+2348012345678',
  ),
);

To identify someone after the chat is already open (e.g. they log in mid-conversation), use a controller:

controller.updateUser(ResolveUser(email: 'jane@example.com'));

Identity persists in the WebView's storage: clearing app data resets anonymous visitors, while identified users are re-linked by email.

Unread Counts and Notifications #

Unread counts accrue while the chat is not visible — when it is visible, the page marks incoming messages read. Observe them at app level, so the callback outlives the chat screen:

ResolveLiveChat.preload(
  widgetId: 'YOUR_WIDGET_ID',
  user: user,
  onUnreadCountChanged: (count) => setState(() => _badge = count),
);

This is the callback that fires while the chat is closed, which is when a badge matters. ResolveChatWidget.onUnreadCountChanged only fires while that widget is mounted — right for a chat you keep alive behind a tab, wrong for a badge on a screen the chat is pushed over.

The SDK mirrors app background/foreground into chat visibility automatically (manageAppLifecycle: true). If you hide the chat some other way while keeping it mounted, mirror it yourself:

controller.setChatVisibility(false); // hidden → replies count as unread
controller.setChatVisibility(true);  // visible → badge clears

Playing a sound or showing a banner #

The chat page has its own notification chime, but it can only play while its WebView is on screen — once the chat screen is dismissed the WebView is detached and the platform will not play its audio. To alert the user, raise a local notification from the unread callback above. See example/lib/main.dart for a worked version using flutter_local_notifications.

This covers the chat being closed while the app is open. It cannot cover a backgrounded or terminated app: the OS suspends the WebView and its socket, so no event fires and there is nothing to notify from. That needs backend push support, which is planned.

Keeping events flowing while the chat is closed #

iOS suspends a WebView's JavaScript once its native view leaves the view hierarchy — which is what happens when the chat screen is popped. The socket survives, but the events it would deliver can stall until the chat reopens, so the unread callback above may fire late or not at all in the meantime.

ResolveSessionHost fixes this: it parks each warm session's WebView invisibly (1 logical pixel, behind your UI) so it never leaves the hierarchy and its JavaScript keeps running. Mount it once, above the navigator:

MaterialApp(
  builder: (context, child) => ResolveSessionHost(child: child!),
  home: ...,
)

That is the whole integration — chat screens borrow the WebView from the host and hand it back automatically. Recommended for any app that shows an unread badge or raises notifications from the callback above.

Warm Start #

The chat WebView is cached per widget ID and outlives the screen showing it, so closing the chat only hides it: reopening is instant and keeps the conversation, scroll position and socket. Preload it (typically after login) so the first open is instant too:

ResolveLiveChat.preload(widgetId: 'YOUR_WIDGET_ID', user: user);

Clear it when the user signs out:

ResolveLiveChat.logout();

A cached session is bound to the identity it was created for, so opening the chat as a different user rebuilds it — one user can never see another's conversation. logout is still worth calling: it removes the conversation at sign-out rather than at the next open, frees the WebView's memory and socket, and is the only way to clear a session when the next user is anonymous.

Pass keepAlive: false to open or ResolveChatWidget for a throwaway WebView destroyed with the screen.

One WebView can only be displayed in one place, so if you ever show two chat screens at once the second loads its own copy instead of sharing the warm one.

Custom Layouts #

For tabs, split views, or a chat you keep mounted, use the widget directly:

ResolveChatWidget(
  widgetId: 'YOUR_WIDGET_ID',
  user: ResolveUser(email: 'jane@example.com'),
  onChatClosed: () => Navigator.of(context).pop(),
  onError: (event) => log('chat failed: ${event.errorCode}'),
)

It fills whatever constraints it is given. While the page boots it shows ResolveChatSkeleton, a placeholder of the chat's own layout; override it with loadingBuilder.

File Attachments (Android) #

iOS presents its file picker natively. Android WebViews delegate picking to the host app — provide onAttachFile and return local file URIs, e.g. with file_picker:

ResolveChatWidget(
  widgetId: '...',
  onAttachFile: (request) async {
    final result = await FilePicker.platform.pickFiles(
      allowMultiple: request.allowMultiple,
    );
    return result?.files.map((f) => Uri.file(f.path!).toString()).toList() ?? [];
  },
)

Navigation away from the chat page is blocked. To open external links, handle onExternalUrl with url_launcher:

onExternalUrl: (url) => launchUrl(url, mode: LaunchMode.externalApplication),

API Reference #

ResolveLiveChat #

  • open(context, {widgetId, user, controller, onError, onAttachFile, onExternalUrl, baseUrl, useSafeArea, keepAlive}) — push a full-screen chat route; pops itself when the user taps close
    • useSafeArea (bool, default false) — inset within the status bar and home indicator instead of filling the screen
    • keepAlive (bool, default true) — reuse the cached WebView across opens
    • maxSessionAge (default 2h) / maxBackgroundDuration (default 30m) — reload a reused session that is older than this, or after the app was backgrounded this long; null disables either check
  • preload({widgetId, user, baseUrl, onUnreadCountChanged, maxSessionAge, maxBackgroundDuration}) — load the chat in the background so the first open is instant; keeps it hidden so unread accrues
  • logout({widgetId}) — clear the signed-in user's chat; call from sign-out
  • release({widgetId}) — same operation as logout, named for reclaiming memory

ResolveSessionHost #

Mount once above the navigator (via MaterialApp.builder) to keep warm sessions' JavaScript running while no chat screen is mounted — see Keeping events flowing while the chat is closed.

ResolveChatWidget #

Parameter Description
widgetId (required) Widget ID from the Resolve page in your Termii dashboard
user Identify the user as the page loads
controller ResolveChatController for visibility, user updates, reload
baseUrl Override the widget host (self-hosted or staging)
onReady Chat UI finished mounting inside the page
onChatClosed User tapped the chat's own close control
onUnreadCountChanged Unread count changed. Fires only while mounted — for badges use ResolveLiveChat.preload
onError Chat failed to initialize (bad widget ID, config fetch failure)
onEvent Every bridge event, including ones this version doesn't map
onAttachFile Android only: handle attachment picking
onExternalUrl A link points outside the widget host
manageAppLifecycle (default true) Mirror app lifecycle into chat visibility
keepAlive (default true) Reuse the cached WebView instead of reloading
loadingBuilder Replace the default ResolveChatSkeleton placeholder
readyTimeout (default 10s) Reveal the page even if it never reports ready
maxSessionAge (default 2h) Reload a reused session older than this; null disables
maxBackgroundDuration (default 30m) Reload after the app was backgrounded this long; null disables

ResolveChatController #

  • setChatVisibility(bool) — show or hide the chat
  • updateUser(ResolveUser) — identify the user after the page has loaded
  • reload() — reload the chat page

ResolveChatSession #

The cached WebView behind a widget ID. ResolveLiveChat.preload / logout cover the common cases; reach for this to observe events with no chat screen mounted.

  • addListener / removeListener — receive bridge events while the chat is closed
  • setVisibility(bool), identify(ResolveUser), reload()
  • release({widgetId}) (static) — destroy cached sessions

ResolveUser #

  • email (string, required) — user's email address; identity is keyed on this
  • name (string, optional)
  • phone (string, optional) — international format, e.g. +2348012345678

ResolveChatEvent #

Bridge events delivered to onEvent, with type one of ready, chatClosed, unreadCountChanged, error, unknown. Accessors: unreadCount, errorCode, errorMessage.

Example #

See example/ — run with your own widget ID:

cd example
flutter run --dart-define=RESOLVE_WIDGET_ID=YOUR_WIDGET_ID

The example demonstrates preloading, unread badges, local notifications, and clearing the session on sign-out.

Documentation #

  • 📖 Integration Guide — detailed integration examples

Support #


Built with ❤️ by Termii

3
likes
160
points
228
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Termii Resolve Live Chat SDK for Flutter. Add Resolve in-app customer support to any Flutter app in a few lines, with user identification and unread-count events.

Homepage
Repository (GitHub)

License

MIT (license)

Dependencies

flutter, webview_flutter, webview_flutter_android, webview_flutter_wkwebview

More

Packages that depend on termii_resolve_livechat