Widget Chat - AI Chatbot Widget

pub package pub points platform

A beautiful, customizable AI chatbot widget you can drop into any Flutter, FlutterFlow, React, or web app — rich content, tools, multimodal input, and multi-language, all configured from a no-code dashboard at widget-chat.com.

Widget-Chat AI chat widget open on a website

💬 Try the live widget at widget-chat.com (bottom-right) — it runs this exact package.

Features

  • 🎨 Beautiful UI: Modern, customizable design with smooth animations
  • 🚀 Easy Integration: Single widget integration with minimal setup
  • 🔒 Secure: Project-based authentication with secure keys
  • 📱 Responsive: Adaptive design for mobile and web platforms
  • Real-time: Live chat with typing indicators
  • 🎭 Customizable: Themes, colors, FAB styles, and behavior
  • 🌐 Multi-platform: Works on iOS, Android, and Web

Installation

Add to your pubspec.yaml:

dependencies:
  widget_chat: ^0.0.10

Quick Start

1. Import the package

import 'package:widget_chat/widget_chat.dart';

2. Add the ChatWidget to your app

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // Your app content here
          YourMainContent(),
          
          // Add the chatbot
          Positioned(
            bottom: 20,
            right: 20,
            child: ChatWidget(
              configuration: BotConfiguration(
                projectSecretKey: 'your-project-secret-key',
                userID: 'unique-user-id',
                name: 'Support Bot',
                welcomeMessage: 'Hello! How can I help you today?',
              ),
            ),
          ),
        ],
      ),
    );
  }
}

Configuration Options

Basic Configuration

BotConfiguration(
  // Required
  projectSecretKey: 'your-secret-key',  // Get from your dashboard
  userID: 'user-123',                   // Unique identifier for the user
  
  // Optional
  name: 'Assistant',                    // Bot display name
  welcomeMessage: 'How can I help?',    // Initial greeting
  fontFamily: 'Roboto',                 // Chat font (default: Roboto)
  color: '#3B82F6',                     // Primary color in hex
  systemInstructions: 'Be helpful',     // Bot behavior instructions
  isPreviewMode: false,                 // Preview mode flag
)

FAB Customization

BotConfiguration(
  projectSecretKey: 'your-key',
  userID: 'user-123',
  fabConfiguration: FabConfiguration(
    icon: 'chat_bubble',           // Icon name
    iconSize: 24.0,                // Icon size
    iconColor: '#FFFFFF',          // Icon color in hex
    backgroundColor: '#3B82F6',     // Background color in hex
    buttonSize: 56.0,              // FAB size
    borderRadius: 28.0,            // Corner radius
    useAvatarAsIcon: false,        // Use avatar image as icon
  ),
)

Advanced Usage

Responsive Design

// Mobile (default) - full-screen chat experience
ChatWidget(
  configuration: BotConfiguration(
    projectSecretKey: 'your-key',
    userID: 'user-123',
  ),
)

// Web/Desktop - fixed 400x700 chat window
ChatWidget(
  configuration: BotConfiguration(
    projectSecretKey: 'your-key',
    userID: 'user-123',
  ),
  mobile: false,
)

Custom FAB Widget

ChatWidget(
  configuration: BotConfiguration(
    projectSecretKey: 'your-key',
    userID: 'user-123',
  ),
  fabWidget: CustomFABWidget(),  // Your custom FAB
)

Chat State Callbacks

ChatWidget(
  configuration: BotConfiguration(
    projectSecretKey: 'your-key',
    userID: 'user-123',
  ),
  onOpen: ({required bool isOpen}) {
    print('Chat is ${isOpen ? "open" : "closed"}');
    // Track analytics, update UI, etc.
  },
)

Host App Integration

The bot can drive your app — and your app can feed the bot — through four channels. All of them are optional; wire only what your product needs.

Rich-content action callbacks

Backend tools and the model can emit buttons/cards whose taps land in YOUR code. Declare the callback ids your app handles in the dashboard project's callback registry (so the model can't invent ids you don't dispatch), then:

ChatWidget(
  configuration: BotConfiguration(projectSecretKey: '...', userID: 'user-123'),
  onRichContentCallback: (callbackId, payload) {
    switch (callbackId) {
      case 'open_item':    navigateToItem(payload?['item']);
      case 'open_paywall': showPaywall();
      case 'open_login':   showSignIn();
    }
  },
)

Tip — for navigation, prefer ONE generic id over per-feature ids: declare a single open_screen callback whose payload['screen'] selects the destination, and keep a {screenKey: route} map host-side. New destinations then need no new callback ids — just a new key in the map and in the registry description (which is also what tells the model the allowed keys):

case 'open_screen':
  final route = screenRoutes[payload?['screen']]; // {'studio': StudioPage.route, ...}
  if (route != null) Navigator.of(context).push(route());

Authenticated tool calls (authToken)

Pass the END USER's JWT and the backend forwards it as a Bearer header to every tool call, so your API attributes actions to the right account:

BotConfiguration(
  userID: user.id.toString(),
  authToken: session.jwt, // re-read on EVERY send — refresh it proactively
)

authToken is re-read per message: rotate it mid-session with updateConfiguration/a rebuild and the next tool call uses the fresh token. Refresh BEFORE expiry (e.g. a 5-minute periodic check) — an expired token makes every gated tool 401 and degrades the conversation.

Guest / anonymous sessions

userID is any non-empty string — no account required. The recommended pattern for apps with optional sign-in:

  • Guests get a device-persistent id (anon-<random>, stored in prefs) and NO authToken.
  • Tell the bot who it's talking to via userContext ({'authenticated': false}), and gate account-bound tools in your system prompt AND in each tool's description ("SIGNED-IN USERS ONLY … emit the open_login button instead"). A required boolean parameter on gated tools (e.g. user_is_signed_in, "copy user_context.authenticated exactly") makes small models respect the gate far more reliably than prose alone.
  • On login, remount the chat keyed to the real user id — history scopes per userID, so the guest thread stays behind and the account thread loads.
  • Disable attachments for guests (enableFileAttach: false) when your upload endpoint needs auth.

User context (userContext)

A free-form map injected into the system prompt on every send — entitlement flags, profile ids, supported callback vocabulary. Keep it small (tokenised each turn, 8 KB cap):

userContext: {
  'authenticated': true,
  'pro': user.isPro,
  'supported_callbacks': ['open_item', 'open_paywall'],
}

Host slots

Backend tools with execution_target: host_render reserve a spot in the bubble that YOUR widget fills — the tool's arguments arrive as the payload:

hostSlotBuilder: (slotId, payload) =>
    slotId == 'item_preview' ? ItemPreview(payload) : null,

Slot builders must be pure functions of the payload — history reloads re-render them from stored JSON.

Image uploads

Wire imageUploader to your own storage and the chat sends compact URLs instead of inlining base64 into the conversation:

imageUploader: (bytes, mimeType) async => myCdn.upload(bytes, mimeType),

Attached photos are forwarded by the backend to image-bearing tools (analysis, extraction, try-on style flows) — the model never handles raw bytes.

Theming that stays on brand

Since 0.0.18 the chat pins its accent to your configured brand color in BOTH light and dark mode (filled buttons, send icon, focus rings render the exact color instead of a Material tonal derivative). Per-mode surfaces and bubble colors come from the dashboard (chat_appearance / chat_appearance_dark), with defaultThemeMode and an optional in-chat theme switcher (showThemeToggle). Prefer configuring colors on the dashboard over hard-coding them host-side — config survives app releases.

Platform-Specific Setup

Mobile (Default)

The mobile parameter defaults to true, so the chat opens in full-screen mode out of the box - ideal for iOS and Android apps.

Web / Desktop

For web or desktop applications, set mobile: false to use a fixed-size (400x700) chat window:

ChatWidget(
  configuration: config,
  mobile: false,
)

The embedded iframe version (web_embed) already handles this automatically via URL parameters.

Available Icons

The FAB supports these icon options:

  • chat_bubble (default)
  • chat
  • message
  • support_agent
  • help
  • question_answer
  • forum
  • contact_support
  • headset_mic

Security Best Practices

  1. Never expose your project secret key in client-side code
  2. Use unique user IDs to maintain conversation history
  3. Configure allowed origins for web deployments
  4. Keep your SDK updated for security patches

Example App

Check out the complete example in the /example folder:

import 'package:flutter/material.dart';
import 'package:widget_chat/widget_chat.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Widget Chat Example',
      home: Scaffold(
        body: Stack(
          children: [
            Center(
              child: Text('Your App Content'),
            ),
            Positioned(
              bottom: 20,
              right: 20,
              child: ChatWidget(
                configuration: BotConfiguration(
                  projectSecretKey: 'your-project-secret-key',
                  userID: 'demo-user',
                  name: 'AI Assistant',
                  welcomeMessage: 'Hi! How can I help you today?',
                  color: '#6366F1',
                  fabConfiguration: FabConfiguration(
                    icon: 'support_agent',
                    backgroundColor: '#6366F1',
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Troubleshooting

Chat doesn't open

  • Verify your project secret key is correct
  • Check console for any error messages
  • Ensure the widget is properly positioned in your widget tree

Styling issues

  • Make sure hex colors include the # symbol
  • Use supported Google Fonts for fontFamily
  • Check that size values are reasonable (e.g., buttonSize: 56.0)

Support

License

This project is licensed under the Proprietary License - see the LICENSE file for details.

Libraries

chat_widget
localization/generated/chat_localizations
localization/generated/chat_localizations_ar
localization/generated/chat_localizations_bg
localization/generated/chat_localizations_bn
localization/generated/chat_localizations_ca
localization/generated/chat_localizations_cs
localization/generated/chat_localizations_da
localization/generated/chat_localizations_de
localization/generated/chat_localizations_el
localization/generated/chat_localizations_en
localization/generated/chat_localizations_es
localization/generated/chat_localizations_et
localization/generated/chat_localizations_fa
localization/generated/chat_localizations_fi
localization/generated/chat_localizations_fr
localization/generated/chat_localizations_ga
localization/generated/chat_localizations_he
localization/generated/chat_localizations_hi
localization/generated/chat_localizations_hr
localization/generated/chat_localizations_hu
localization/generated/chat_localizations_id
localization/generated/chat_localizations_it
localization/generated/chat_localizations_ja
localization/generated/chat_localizations_ko
localization/generated/chat_localizations_lt
localization/generated/chat_localizations_lv
localization/generated/chat_localizations_ms
localization/generated/chat_localizations_mt
localization/generated/chat_localizations_nl
localization/generated/chat_localizations_no
localization/generated/chat_localizations_pl
localization/generated/chat_localizations_pt
localization/generated/chat_localizations_ro
localization/generated/chat_localizations_ru
localization/generated/chat_localizations_sk
localization/generated/chat_localizations_sl
localization/generated/chat_localizations_sr
localization/generated/chat_localizations_sv
localization/generated/chat_localizations_sw
localization/generated/chat_localizations_ta
localization/generated/chat_localizations_te
localization/generated/chat_localizations_th
localization/generated/chat_localizations_tl
localization/generated/chat_localizations_tr
localization/generated/chat_localizations_uk
localization/generated/chat_localizations_vi
localization/generated/chat_localizations_zh
test