vinci_ai_support 0.2.1
vinci_ai_support: ^0.2.1 copied to clipboard
Vinci AI support widget SDK for Flutter applications.
Vinci AI Flutter SDK #
Flutter SDK for embedding Vinci AI support in Flutter apps.
The package renders the Vinci support widget as native Flutter UI. It fetches server-side widget configuration, applies branding, starts and restores conversations, sends messages, uploads attachments, collects contact email for handoff, and exposes a controller for host-owned buttons or routes.
Native Android and iOS apps integrate Vinci through Flutter add-to-app: the native host embeds the Vinci Flutter module and presents the same support UI.
Requirements #
- Flutter 3.24 or later.
- Dart 3.5 or later.
- Android and iOS are the supported mobile targets for v1.
Install #
Install from pub.dev:
flutter pub add vinci_ai_support
dependencies:
vinci_ai_support: ^0.1.5
Import the SDK:
import 'package:vinci_ai_support/vinci_ai_support.dart';
Quick Start #
Create the API client with the same public values used by the Vinci web widget:
final supportClient = VinciApiSupportClient(
config: VinciApiSupportConfig(
apiBaseUrl: 'https://api.vinci.example',
agentId: 'agent-id',
agentKey: 'vinci_pk_public_key',
),
);
Create a controller and wrap your app shell with VinciSupportLauncher:
final supportController = VinciSupportController(
client: supportClient,
sessionStore: VinciSharedPreferencesSupportSessionStore.withNamespace(
'vinci_support',
),
);
return VinciSupportLauncher(
controller: supportController,
onReady: () {
// The support surface mounted and stored session bootstrap completed.
},
onOpen: () {
// The support surface transitioned from closed to open.
},
onClose: () {
// The support surface transitioned from open to closed.
},
onCloseRequested: () {
// Dismiss any host-owned route or surface here.
},
child: const MyAppShell(),
);
Dispose the controller with the host widget lifecycle:
@override
void dispose() {
supportController.dispose();
super.dispose();
}
The default launcher renders the Vinci floating support button. When opened, it loads remote widget config and applies branding, copy, FAQs, and feature flags returned by the public widget config endpoint.
Identify A Signed-In User #
Anonymous use works without app user fields. For signed-in users, pass stable host-app identity to the controller:
final supportController = VinciSupportController(
client: supportClient,
user: const VinciSupportUser(
id: 'user-123',
email: 'ada@example.com',
name: 'Ada Lovelace',
attributes: <String, Object?>{
'plan': 'pro',
'workspaceId': 'workspace-123',
},
),
sessionStore: VinciSharedPreferencesSupportSessionStore.withNamespace(
'vinci_support',
),
);
If the Vinci workspace requires signed identity verification, request the identity token from your backend after sign-in and pass it in the API config:
final supportClient = VinciApiSupportClient(
config: VinciApiSupportConfig(
apiBaseUrl: 'https://api.vinci.example',
agentId: 'agent-id',
agentKey: 'vinci_pk_public_key',
identity: VinciSupportIdentity.signedUser(token: userIdentityToken),
),
);
Do not mint signed identity tokens in the mobile app. The token must come from
the customer backend. If identity verification is not enabled for the workspace,
omit identity.
Open Support From Your Own UI #
Use the standard floating button when Vinci should own the visible entry point:
return VinciSupportLauncher(
controller: supportController,
config: const VinciSupportConfig(
behavior: VinciSupportBehavior(
launcherMode: VinciSupportLauncherMode.floatingButton,
position: VinciSupportPanelPosition.bottomRight,
),
),
child: const MyAppShell(),
);
Use custom launcher mode when the app already has a support row, settings
button, order action, or account menu item:
return VinciSupportLauncher(
controller: supportController,
config: const VinciSupportConfig(
behavior: VinciSupportBehavior(
launcherMode: VinciSupportLauncherMode.custom,
),
),
child: SettingsScreen(
onContactSupport: supportController.show,
),
);
You can also control the panel directly:
supportController.open();
supportController.close();
supportController.toggle();
Presentation Modes #
Full-Screen Route #
Use a full-screen route when support should behave like a native support screen:
await Navigator.of(context).push(
MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (panelContext) => Scaffold(
body: SafeArea(
child: VinciSupportPanel(
controller: supportController..show(),
onCloseRequested: () => Navigator.of(panelContext).pop(),
),
),
),
),
);
Modal Bottom Sheet #
Use a modal bottom sheet when support should feel like a native sheet:
await showModalBottomSheet<void>(
context: context,
isScrollControlled: true,
useSafeArea: true,
builder: (sheetContext) => VinciSupportPanel(
controller: supportController..show(),
onCloseRequested: () => Navigator.of(sheetContext).pop(),
),
);
Inline Surface #
Use inline mode for a dedicated support tab, account section, or split view:
SizedBox(
height: 640,
child: VinciSupportPanel(
controller: supportController..show(),
config: const VinciSupportConfig(
behavior: VinciSupportBehavior(
launcherMode: VinciSupportLauncherMode.custom,
),
),
),
);
Lifecycle Callbacks #
Both VinciSupportLauncher and VinciSupportPanel expose
the same lifecycle callbacks:
onReadyfires once after the surface mounts and stored session bootstrap completes. Remote config and history remain lazy until the surface opens.onOpenfires on every closed-to-open transition.onClosefires on every open-to-closed transition.onCloseRequestedfires when the user taps an SDK close button. The SDK invokes this intent callback before closing its internal controller state; use it to dismiss a host-owned route, bottom sheet, or native add-to-app surface.
Use supportController.open() and supportController.close() for
programmatic control. They invoke onOpen and onClose only when state
actually changes. Programmatic close() does not invoke onCloseRequested
because it is not a user close request. Call
await supportController.initialize() when a host needs to complete the same
local session bootstrap before mounting a Vinci presentation widget.
Remote Widget Config #
VinciApiSupportClient.fetchWidgetConfig() reads server-side widget
configuration. VinciSupportConfig.fromWidgetConfig(...) maps it to the native
Flutter UI.
| Remote field | SDK behavior |
|---|---|
colorPrimary |
Primary color and user message bubble color |
position |
Floating launcher and panel side |
welcomeMessage |
Header subtitle and assistant welcome copy |
headerText |
Chat header title |
homeTitle |
Home empty-state heading |
messageCtaLabel |
Start-message CTA |
logoUrl |
Home logo |
faqs |
Curated FAQ list and article reader |
anonymousEmailCaptureEnabled |
Enables pre-escalation email capture for anonymous users |
videoRecordingEnabled |
Enables video file uploads in the attachment menu |
callBooking |
Booking actions on Home and recovery actions |
zIndex |
Parsed for schema compatibility; Flutter stacking is owned by the host widget tree |
Use local config only for native-specific overrides:
final remoteConfig = await supportClient.fetchWidgetConfig();
VinciSupportLauncher(
controller: supportController,
config: VinciSupportConfig.fromWidgetConfig(
remoteConfig,
baseCopy: const VinciSupportCopy(
homeTabLabel: 'Home',
messagesTabLabel: 'Messages',
),
),
child: app,
);
Configuration Reference #
VinciApiSupportConfig #
| Option | Required | Default | Description |
|---|---|---|---|
apiBaseUrl |
yes | - | Vinci public API base URL. |
agentId |
yes | - | Agent/workspace id used by public widget API routes. |
agentKey |
yes | - | Public widget/agent key. |
origin |
no | null |
Browser-like origin for local/web-style API testing. Flutter apps normally omit it. |
identity |
no | VinciSupportIdentity.anonymous() |
Anonymous, cookie-compatible, or signed identity mode. |
requestTimeout |
no | 20 seconds |
Per-request timeout. |
retryPolicy |
no | VinciRetryPolicy() |
Retry count/backoff for retryable API failures. |
VinciSupportController #
| Option | Required | Description |
|---|---|---|
client |
yes | VinciSupportClient implementation, usually VinciApiSupportClient. |
user |
no | Signed-in app user fields. Omit for anonymous visitors. |
conversationId |
no | Start from a known conversation id. |
visitorId |
no | Start from a known visitor id. |
sessionStore |
no | Session persistence. Use VinciSharedPreferencesSupportSessionStore for normal apps. |
attributes |
no | Initial app-specific attributes included with support requests. |
VinciSupportBehavior #
| Option | Default | Description |
|---|---|---|
position |
bottomRight |
Floating launcher/panel edge: bottomRight or bottomLeft. |
launcherMode |
floatingButton |
floatingButton for SDK-owned entry point, custom for host-owned buttons. |
keyboardInsetMode |
liftPanel |
liftPanel moves the panel above the keyboard; coverBottomChrome lets the keyboard cover bottom chrome while the composer follows it. |
initiallyOpen |
false |
Opens the panel after launcher mount. |
closeOnScrimTap |
true |
Allows closing by tapping outside the panel. |
anonymousEmailCaptureEnabled |
false |
Enables email capture for anonymous handoff flows. Usually set from remote config. |
panelMaxWidth |
380 |
Maximum floating panel width. |
panelHeight |
600 |
Floating panel height. |
edgePadding |
24 |
Floating launcher/panel distance from safe edges. |
Conversations And Runtime API #
The hosted API client supports:
- server-side widget config;
- conversation creation and session restoration;
- SSE chat responses with assistant deltas and workflow messages;
- quick replies and workflow buttons;
- conversation history and conversation list views;
- contact email collection for handoff/escalation;
- conversation rating through CSAT;
- image, file, and video attachment upload when enabled.
Common controller operations:
await supportController.sendMessage('I need help with billing');
await supportController.retryLastFailedMessage();
await supportController.submitContactEmail('ada@example.com');
await supportController.submitConversationRating(score: 5);
Listen to controller state through ChangeNotifier:
supportController.addListener(() {
final isOpen = supportController.isOpen;
final messages = supportController.messages;
final status = supportController.connectionStatus;
});
Attachments #
The built-in attachment picker supports:
- Images: PNG, JPEG, WebP, GIF, up to 10 MB.
- Files: PDF, text, CSV, JSON, Word, and Excel files, up to 25 MB.
- Videos: WebM and MP4, up to 50 MB, when remote
videoRecordingEnabledis true.
The core SDK does not ship camera or screen-recording dependencies. Users can attach an already recorded video file, or the host app can upload a recorded file through the controller:
await supportController.sendAttachment(
type: VinciSupportAttachmentType.video,
bytes: videoBytes,
mime: 'video/mp4',
name: 'walkthrough.mp4',
durationMs: durationMs,
);
Proactive Workflows #
Use VinciProactiveWorkflowController when the app should report screen visits
and open claimed workflows after the API-provided dwell timer:
final proactiveController = VinciProactiveWorkflowController(
client: supportClient,
supportController: supportController,
sessionId: 'stable-session-id',
);
For Navigator-based apps, register the observer once at the app root:
MaterialApp(
navigatorObservers: <NavigatorObserver>[
VinciProactiveNavigatorObserver(controller: proactiveController),
],
home: const AppHome(),
);
Custom router integrations can call reportScreen(...) directly:
await proactiveController.reportScreen(
Uri.parse('myapp://screen/billing'),
);
Native Android And iOS Apps #
Native apps do not install separate Vinci Android or iOS SDK artifacts. They embed the Vinci Flutter module through Flutter add-to-app, then open the module from a native button, menu item, route, or settings screen.