Flutter Chatbot SDK

View Release Notes →

dialogs_chatbot_flutter is a Flutter plugin that embeds a fully functional EnableX Dialogs chatbot into any iOS or Android application. It's built on a WebView-Bridge architecture — the chat protocol runs inside a bundled HTML/JS page, and Dart communicates with it through a bidirectional JavaScript bridge. Zero native Swift/Kotlin code is required — the plugin is framework-agnostic and works with Provider, Riverpod, BLoC, GetX, or plain setState.

Flutter Chatbot SDK v1.0.0  ·  Released June 25, 2026

First release of the Flutter Chatbot SDK, published on pub.dev. See Release Notes for what's included.

  View on pub.dev
Setup

Prerequisites

Flutter & Dart

ToolMinimum Version
Flutter3.10.0
Dart3.0.0

iOS

RequirementValue
Minimum iOS version12.0
NSCameraUsageDescriptionRequired in Info.plist for file/image upload
NSMicrophoneUsageDescriptionRequired in Info.plist for audio/video calling
NSPhotoLibraryUsageDescriptionRequired in Info.plist for gallery access

Android

RequirementValue
minSdkVersion21 (Android 5.0)
CAMERA permissionRequired in AndroidManifest.xml for file upload
RECORD_AUDIO permissionRequired for audio/video calling
READ_EXTERNAL_STORAGERequired for file picker (Android < 13)
READ_MEDIA_IMAGESRequired for image picker (Android 13+)

Installation

Add Dependency

In the host app's pubspec.yaml:

dependencies:
  dialogs_chatbot_flutter: ^1.0.0

  # Required peer dependency — add to host app
  flutter_inappwebview: ^6.1.5

Run:

flutter pub get

iOS Setup

In ios/Runner/Info.plist, add:

<key>NSCameraUsageDescription</key>
<string>Camera access is required to send photos and make video calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for audio and video calls.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo library access is required to send images.</string>

In ios/Podfile, ensure minimum iOS version:

platform :ios, '12.0'

Android Setup

In android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.INTERNET" />

In android/app/build.gradle:

android {
    defaultConfig {
        minSdkVersion 21
    }
}
Usage Patterns

Pattern 1 — Simplest (Screen, no controller)

import 'package:dialogs_chatbot_flutter/dialogs_chatbot_flutter.dart';

Navigator.push(context, MaterialPageRoute(
  builder: (_) => EnxChatScreen(
    config: const EnxBotConfig(
      botId: 'YOUR_BOT_ID',
      host: 'https://dialogs.enablex.io',
    ),
  ),
));

Pattern 2 — Embedded Widget

Scaffold(
  body: EnxChatWidget(
    config: const EnxBotConfig(
      botId: 'YOUR_BOT_ID',
      host: 'https://dialogs.enablex.io',
    ),
    theme: EnxChatTheme.mobileClassic,
    onMessageReceived: (msg) => print(msg.text),
  ),
)

Pattern 3 — External Controller (full control)

class ChatPage extends StatefulWidget {
  const ChatPage({super.key});
  @override
  State createState() => _ChatPageState();
}

class _ChatPageState extends State {
  late final EnxChatController _controller;

  @override
  void initState() {
    super.initState();
    _controller = EnxChatController(
      config: const EnxBotConfig(
        botId: 'YOUR_BOT_ID',
        host: 'https://dialogs.enablex.io',
        callingEnabled: true,
      ),
    );
    _controller.onStatusChanged = (s) => setState(() {});
    _controller.onError = (msg, type) => _showError(msg);
    _controller.initialize();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => EnxChatWidget.withController(
    controller: _controller,
    showCallingOptions: true,
    onAudioCall: () => /* your call SDK */,
    onVideoCall: () => /* your call SDK */,
  );
}

Pattern 4 — BLoC / Stream Integration

// In your BLoC:
final _sub = controller.messageStream.listen((msg) => add(BotMessageEvent(msg)));
final _statusSub = controller.statusStream.listen((s) => add(ConnectionEvent(s)));

// In dispose:
await _sub.cancel();
await _statusSub.cancel();
controller.dispose();
Configuration

Configuration Reference

EnxBotConfig Fields

FieldTypeRequiredDescription
botIdStringYesUnique bot ID from EnableX dashboard
hostStringYesBackend base URL
rootPathStringNoURL path prefix (default '')
callingEnabledboolNoShow call buttons (default false)

Theme Customisation

const EnxChatTheme myTheme = EnxChatTheme(
  primaryColor: Color(0xFF1A73E8),          // Google Blue
  userBubbleColor: Color(0xFFE8F0FE),
  botBubbleColor: Colors.white,
  backgroundColor: Color(0xFFF5F5F5),
  userAvatarColor: Color(0xFF1A73E8),
  botAvatarColor: Color(0xFF34A853),
  useMobileClassicLayout: false,
);

For a WhatsApp-style layout:

theme: EnxChatTheme.mobileClassic
Handling Messages

Message Types — Developer Reference

When onMessageReceived fires, check message.type to handle each case:

controller.onMessageReceived = (EnxMessage msg) {
  switch (msg.type) {
    case EnxMessageType.text:
      // msg.text — plain text
      // msg.actions — optional action buttons
      break;

    case EnxMessageType.suggestion:
      // msg.choices — List
      // msg.suggestionType — single or multiple
      break;

    case EnxMessageType.image:
      // msg.media?.url — CDN image URL
      break;

    case EnxMessageType.video:
      // msg.media?.url — video URL
      break;

    case EnxMessageType.file:
      // msg.media?.url, msg.media?.fileName, msg.media?.mimeType
      break;

    case EnxMessageType.system:
      // msg.handoverStatus — handover lifecycle phase
      break;

    case EnxMessageType.typing:
      // Handled internally — shows EnxTypingBubble
      break;

    default:
      break;
  }
};

Sending Messages Programmatically

// Plain text
await _controller.sendText('What are your hours?');

// Single suggestion chip (after user taps)
await _controller.sendChoice(EnxChoice(title: 'Yes', value: 'yes'));

// Multiple suggestion chips
await _controller.sendMultiChoice([
  EnxChoice(title: 'Option A', value: 'a', isSelected: true),
  EnxChoice(title: 'Option B', value: 'b', isSelected: true),
]);

// File upload
final bytes = await File(path).readAsBytes();
await _controller.sendFile(
  bytes: bytes,
  fileName: 'report.pdf',
  mimeType: 'application/pdf',
);

// Reset conversation
await _controller.resetConversation();
Video Call Integration

Mode A — Bot-Initiated (Automatic)

The bot sends a message with audioVideoAction = "start" and iFrameUrl. The plugin automatically shows EnxVideoCallOverlay. No host app code needed.

Mode B — Button-Initiated (Manual)

Set callingEnabled: true and provide callbacks:

EnxChatWidget(
  config: config,
  showCallingOptions: true,
  onAudioCall: () => YourCallSDK.startAudio(),
  onVideoCall: () => YourCallSDK.startVideo(),
)
Architecture

Plugin Internal Architecture

JS Bridge Communication

The bridge uses Flutter's JavascriptChannel API (named "EnxChannel"). All messages are JSON strings with the shape:

{
  "event": "message",
  "data": { ... }
}

Events Received from JS

EventDataDescription
chatReadyHTML fully loaded
connectedSocket connected
disconnectedSocket dropped
reconnectingAuto-reconnect started
reconnectedReconnect succeeded
reconnectFailedReconnect failed
botInfoMap<String, dynamic>Bot metadata
conversationReady{ conversationId: string }Conversation initialised
messageArray<MessageObject>One or more messages
typing{ duration: number }Typing indicator
clear{ conversationId?: string }Conversation cleared
messageSentUser message confirmed by server
fileSentFile upload confirmed
error{ message: string, type: string }Error from JS

Commands Sent to JS

window.sendTextMessage(text)
window.sendSuggestion(value)
window.sendMultiSuggestions([value1, value2])
window.sendFileMessage(base64DataURI, fileName, mimeType)
window.resetConversation()
window.reconnect()
window.disconnect()

Platform-Specific WebView Setup

The plugin automatically applies platform-specific WebView configuration via enx_webview_media_config.dart:

Android

PlatformWebViewControllerCreationParams enxWebViewCreationParamsForMedia()
// Returns AndroidWebViewControllerCreationParams with:
// - allowFileAccess: true
// - javaScriptCanOpenWindowsAutomatically: true

void enxConfigureAndroidWebViewMediaPermissions(WebViewController controller)
// Configures:
// - mediaPlaybackRequiresUserGesture: false
// - onPermissionRequest: auto-grant camera/mic

iOS

// Returns WebKitWebViewControllerCreationParams with:
// - allowsInlineMediaPlayback: true
// - mediaTypesRequiringUserActionForPlayback: []

File Structure

lib/
├── dialogs_chatbot_flutter.dart          ← Single import barrel
└── src/
    ├── models/
    │   ├── enx_bot_config.dart            ← Bot configuration data object
    │   └── enx_message.dart               ← All message models + enums
    ├── controller/
    │   └── enx_chat_controller.dart       ← State, streams, send methods
    ├── bridge/
    │   └── enx_webview_bridge.dart        ← JS↔Dart bridge
    ├── utils/
    │   └── enx_webview_media_config.dart  ← Platform WebView config
    └── views/
        ├── enx_chat_widget.dart            ← Embeddable widget
        ├── enx_chat_screen.dart            ← Full-screen scaffold
        ├── enx_chat_list.dart              ← Scrollable message list
        ├── enx_message_cell.dart           ← Per-message renderer + theme
        ├── enx_input_bar.dart              ← Input + attachments + call buttons
        ├── enx_typing_bubble.dart          ← Animated 3-dot indicator
        └── enx_video_call_overlay.dart     ← Agent video WebView (InAppWebView)
Feature Support

Feature Support (v1.0.0)

FeatureSupportedNotes
Full chat UIYesMessage list (EnxChatList) and input bar (EnxInputBar), bot avatar/name from the bridge's botInfo payload, animated typing indicator.
Theme customizationYesColour tokens via EnxChatTheme, including a built-in mobileClassic (WhatsApp-style) layout.
Text messagesYesPlain text, optionally with action link buttons.
Suggestion chips (single/multi-select)Yes 
Image / video / file attachmentsYesImage cards, video cards with thumbnail + play overlay, file attachment cards; sent via image_picker / file_picker.
Calendar (date picker) promptsYes 
System / handover status messagesYesRendered as centered status messages.
Click-to-call (text / audio / video)YesServer-driven via botInfo.clickToCall — full chat UI for “text”, a call-only UI otherwise (showClickToCallVideoOnlyUi).
Full-screen video call overlayYesEnxVideoCallOverlay via flutter_inappwebview.
Conversation resetYesEnxChatController.resetConversation().
Reactive streams (BLoC-style)YesmessageStream / statusStream on EnxChatController.
Framework-agnostic state managementYesWorks with Provider, Riverpod, BLoC, GetX, or plain setState via the external-controller pattern.
Web / desktop targetsNoiOS and Android only — use the JS SDK directly for web.
UI theming beyond color tokensNoNo layout/structural customization API in v1.0.
Offline message queueNoMessages fail silently when disconnected; monitor statusStream and prompt retry.
Reference

Dependencies

PackageVersionLicensePurpose
webview_flutter^4.13.0BSD-3Chat bridge WebView
flutter_inappwebview^6.1.5Apache-2.0Video call WebView
image_picker^1.2.0BSD-3Camera/gallery
file_picker^11.0.2MITDocument picker
permission_handler^12.0.1MITRuntime permissions
cached_network_image^3.4.0MITImage caching
video_player^2.11.0BSD-3Inline video
url_launcher^6.3.0BSD-3Open action URLs
http^1.6.0BSD-3HTTP requests
mime^2.0.0BSD-3MIME detection
intl^0.20.2BSD-3Date formatting

Known Limitations

LimitationImpactWorkaround
iOS & Android onlyWeb/Desktop not supportedUse the JS SDK directly for web
flutter_inappwebview is a peer dependencyHost app must add itAdd to host pubspec.yaml
Calendar message type not yet implementedEnxMessageType.calendar renders as textPlanned for v1.1.0
No offline queueMessages fail silently when disconnectedMonitor statusStream and prompt retry
Android WebView versionRequires Android System WebView 67+Advise users to update

Troubleshooting

Chat Shows Blank / Spinner Never Resolves

  • Check that botId and host are correct
  • Confirm network connectivity to host
  • Check onError callback for bridge errors

Images/Videos Don't Autoplay on Android

  • Ensure enxConfigureAndroidWebViewMediaPermissions was called (done automatically via bridge)
  • Check that minSdkVersion >= 21

MissingPluginException for flutter_inappwebview

  • Add flutter_inappwebview: ^6.1.5 to the host app's pubspec.yaml
  • Run flutter pub get and rebuild

File Upload Fails on iOS

  • Add all three NS*UsageDescription keys to Info.plist
  • Request permissions before calling sendFile

Black Screen in Video Overlay

  • Confirm the iFrameUrl is HTTPS
  • Add NSAppTransportSecurity exception if needed for development

License

This plugin is proprietary software owned by EnableX / VCloudX Infotech. All rights reserved. Redistribution requires written permission.

Support