BotConfiguration constructor
const
BotConfiguration({
- required String userID,
- required String projectSecretKey,
- String? userIdSignature,
- LocalizedText? welcomeMessage,
- LocalizedText? name,
- LocalizedText? systemInstructions,
- @Default.new('en') String defaultLocale,
- @Default.new(['en']) List<
String> supportedLocales, - String? currentLocale,
- String? allowedOrigins,
- @Default.new('Roboto') String? fontFamily,
- String? color,
- @unfreezed Uint8List? avatar,
- @Default.new(false) bool isPreviewMode,
- @Default.new(false) bool isReadOnly,
- @Default.new(true) bool showAvatar,
- @Default.new(true) bool enableUserExport,
- @Default.new(true) bool enableClearHistory,
- @Default.new(true) bool enableFileAttach,
- @Default.new(false) bool dynamicLanguageEnabled,
- @Default.new(true) bool aiDisclosureEnabled,
- LocalizedText? aiDisclosureText,
- @Default.new(false) bool aiDisclosureRequireAck,
- @Default.new(true) bool messageReportingEnabled,
- String? aiInfoUrl,
- String? privacyUrl,
- @Default.new(false) bool aiDataReuseNotice,
- @Default.new(FabConfiguration()) FabConfiguration fabConfiguration,
- @Default.new(SoundConfiguration()) SoundConfiguration soundConfiguration,
- ChatAppearance? chatAppearance,
- ChatAppearance? chatAppearanceDark,
- @Default.new(true) bool showThemeToggle,
- @Default.new('system') String defaultThemeMode,
- Map<
String, dynamic> ? userContext, - String? authToken,
Implementation
const factory BotConfiguration({
/// Unique identifier for the user interacting with the bot.
required String userID,
/// Public project identifier shipped to the widget. Used by the
/// chat backend to look up the project; safe to inline in HTML or
/// app bundles (it's the equivalent of a Stripe publishable key).
/// By itself it cannot read another user's history — that requires
/// a matching [userIdSignature].
required String projectSecretKey,
/// HMAC-SHA256 hex digest of [userID] keyed by the project's
/// `identity_secret`, computed on the host app's backend and
/// forwarded here.
///
/// The chat backend verifies this server-side before serving
/// `/api/chat/*` for the given [userID]. Without it, anyone who
/// reads the install snippet HTML can pair the public
/// [projectSecretKey] with an arbitrary [userID] and read that
/// user's history.
///
/// Optional for backwards-compat with projects that haven't yet
/// enabled `identity_verification_required` in the dashboard's
/// Data & Privacy section; required once that toggle is on.
///
/// NEVER bake the `identity_secret` itself into the SDK or the
/// host app's binary — compute the signature on your backend and
/// ship only the resulting digest. See the dashboard's Install
/// tab for backend-side snippets in Node, Python, and Ruby.
String? userIdSignature,
/// Initial message displayed when the chat opens (localized).
LocalizedText? welcomeMessage,
/// Display name for the chatbot (localized).
LocalizedText? name,
/// System instructions that guide the bot's behavior (localized).
LocalizedText? systemInstructions,
/// Default locale code for the chat interface (e.g., 'en', 'fr', 'es').
@Default('en') String defaultLocale,
/// List of supported locale codes
@Default(['en']) List<String> supportedLocales,
// The locale code for the chat interface (e.g., 'en', 'fr', 'es').
String? currentLocale,
/// Comma-separated list of allowed origins for CORS.
String? allowedOrigins,
/// Font family for chat text. Defaults to 'Roboto'.
@Default('Roboto') String? fontFamily,
/// Primary color for the chat interface in hex format. When null, the
/// SDK inherits the host app's theme colour (`ColorScheme.primary`) —
/// see `_getPrimaryColor` in chat_screen. Left intentionally without a
/// default so "unset" means "inherit from app" rather than a forced hex.
String? color,
/// Avatar image data. Note: This is excluded from JSON serialization.
@unfreezed Uint8List? avatar,
/// Whether the bot is running in preview mode.
@Default(false) bool isPreviewMode,
/// Whether the chat is in read-only mode (no user input allowed).
/// When true, the chat input field will be disabled.
@Default(false) bool isReadOnly,
/// Whether to show the bot avatar next to bot MESSAGES (and the
/// typing indicator). The header avatar is unconditionally shown —
/// this flag only gates the per-bubble avatar circle.
///
/// When `true` (default) each non-user message is preceded by the
/// configured [avatar] circle. When `false`, the avatar is hidden
/// and the bubble grows a small WhatsApp-style tail at its
/// top-left to keep the sender visually anchored.
///
/// Host apps that want a denser, avatar-less message list (e.g.
/// compact in-app coaches) can flip this without touching the
/// bubble code.
@Default(true) bool showAvatar,
/// Whether to surface an "Export chat" item in the chat header's
/// more-options menu. When `true` (default) the user can download
/// their own transcript as a `.zip` and hand it to the platform
/// share sheet. When `false`, the menu item is hidden — useful for
/// hosts whose privacy posture or app-store category requires
/// suppressing data-export affordances.
///
/// This is also surfaced via the server's `/api/project/config/`
/// payload (`enable_user_export`) so a host can toggle it from
/// the dashboard without a widget redeploy. The host-supplied
/// value here wins per the standard host-overrides-server
/// precedence; see [ChatViewModel.initialize].
@Default(true) bool enableUserExport,
/// Whether to surface a "Clear chat history" item in the chat
/// header's more-options menu. When `true` (default) the user can
/// wipe their own conversation; when `false`, the item is hidden.
///
/// Surfaced via the server's `/api/project/config/` payload
/// (`enable_clear_history`) — same host-overrides-server precedence
/// as [enableUserExport].
@Default(true) bool enableClearHistory,
/// Whether to render the paperclip attach button in the chat
/// input. When `true` (default) the user can pick an image to
/// send with their message; when `false`, the button is hidden.
///
/// Surfaced via the server's `/api/project/config/` payload
/// (`enable_file_attach`) — same host-overrides-server precedence
/// as [enableUserExport].
@Default(true) bool enableFileAttach,
/// When `true`, the bot may switch the chat language live (via the
/// backend `change_language` tool) and the SDK exposes a manual
/// language picker in the header. Surfaced via the server config
/// (`dynamic_language_enabled`). Picker also requires 2+
/// [supportedLocales].
@Default(false) bool dynamicLanguageEnabled,
/// When `true` (default), the chat renders a light "You're chatting
/// with an AI" disclosure inside the chat surface. Surfaced via the
/// server config (`ai_disclosure_enabled`).
@Default(true) bool aiDisclosureEnabled,
/// Per-locale disclosure text. Null = use the SDK's localized
/// default (`ai_disclosure` ARB key). Surfaced via the server config
/// (`ai_disclosure_text`).
LocalizedText? aiDisclosureText,
/// When `true`, the disclosure shows a one-time "OK" the user must
/// acknowledge (persisted locally). Surfaced via the server config
/// (`ai_disclosure_require_ack`).
@Default(false) bool aiDisclosureRequireAck,
/// When `true`, bot messages expose a long-press "Good/Bad response"
/// menu; "Bad response" opens a reason sheet that reports the message
/// (App Store UGC / Guideline 1.2). On by default. Surfaced via the
/// server config (`message_reporting_enabled`).
@Default(true) bool messageReportingEnabled,
/// "Learn more" target for the disclosure chip — an About-AI page.
/// Falls back to [privacyUrl] when empty. Surfaced via the server
/// config (`ai_info_url`).
String? aiInfoUrl,
/// Privacy page link; fallback "Learn more" target. Surfaced via the
/// server config (`privacy_url`).
String? privacyUrl,
/// When `true`, the chip shows an extra line that chats may be used
/// to improve the service (display-only). Surfaced via the server
/// config (`ai_data_reuse_notice`).
@Default(false) bool aiDataReuseNotice,
/// Configuration for the floating action button appearance.
@Default(FabConfiguration()) FabConfiguration fabConfiguration,
/// Configuration for sound effects.
@Default(SoundConfiguration()) SoundConfiguration soundConfiguration,
/// Tenant-configurable chat-surface colours. When the server
/// returns a `chat_appearance` block in `/api/project/config/`,
/// the repository inflates it into this field; hosts can also
/// pass one explicitly to override at the per-session level.
///
/// Render-time precedence (lowest → highest):
/// 1. SDK's MD3 defaults derived from the ambient [ColorScheme].
/// 2. This field (server-set or host-set).
/// 3. `Theme.of(context).extension<ChatColors>()` on the host's
/// MaterialApp — wins when present, lets a host force a
/// brand-specific palette regardless of what the dashboard
/// says.
ChatAppearance? chatAppearance,
/// Dark-mode counterpart to [chatAppearance]. Selected when the active
/// theme brightness is dark. Same precedence rules as [chatAppearance].
ChatAppearance? chatAppearanceDark,
/// Whether the light/dark theme switcher appears in the header's
/// more-options menu. Server-gated via `show_theme_toggle`.
@Default(true) bool showThemeToggle,
/// Initial theme mode before the user overrides it — 'light', 'dark',
/// or 'system' (follows the host device brightness).
@Default('system') String defaultThemeMode,
/// Free-form host-app context forwarded to the bot on every
/// `/api/chat/` request and surfaced into the system prompt.
///
/// Use this to give the LLM facts about the current user that aren't
/// already visible from the message history — e.g. Pro entitlement
/// state, current hand-analysis ID, last design they viewed, skin
/// tone, accessibility preferences. The backend reads it from the
/// request body and inlines it into the system prompt under a
/// dedicated `<user_context>` section.
///
/// Keep payloads small (a few KB max) — every key here is tokenised
/// per turn. Don't include large blobs, image bytes, or PII the bot
/// doesn't need. Map values must be JSON-serialisable.
///
/// Example:
/// ```dart
/// BotConfiguration(
/// userID: '42',
/// projectSecretKey: '...',
/// userContext: {
/// 'pro': true,
/// 'hand_analysis_id': 'h_abc',
/// 'last_design_id': 789,
/// 'skin_tone_fitzpatrick': 4,
/// },
/// )
/// ```
Map<String, dynamic>? userContext,
/// End-user JWT (or equivalent bearer token) forwarded to the bot
/// backend on every chat request so authenticated downstream tools
/// can attribute calls to the right account.
///
/// Use this when the host app has its own identity layer and the bot
/// needs to call a backend API that requires that user's auth — e.g.
/// AI Nail's `/api/nail/*` endpoints, where ``design_save`` must
/// attach to the calling user's library, ``tryon_start`` to their
/// hand photo, etc.
///
/// The token is sent only in-flight, never persisted by widget_chat.
/// Read-side rotation is on the host: a new token here on the next
/// `updateConfiguration` reaches every subsequent request. Pass
/// ``null`` (the default) when the bot has no authenticated tools.
///
/// Backend-side handling is described in
/// `flutterbot_api/chat_api/tools/api_executor.py` — the token lands
/// as `Authorization: Bearer <token>`, never overrides a tool's own
/// service-account header, and is redacted from ToolExecution rows.
String? authToken,
}) = _BotConfiguration;