find_ai_chat 0.4.0 copy "find_ai_chat: ^0.4.0" to clipboard
find_ai_chat: ^0.4.0 copied to clipboard

Platformweb

Flutter SDK for the Find AI chat assistant. Drop-in widget with floating, drawer and embedded modes, SSE streaming, and signed-mode visitor auth.

example/lib/main.dart

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

const String kExampleConnectionId = 'dca2bd9ae5034784b61db61f53c7bbcb';
const String kExampleBaseUrl = 'http://localhost:8000';

void main() {
  FindChatEnvironment.configure(baseUrl: kExampleBaseUrl);
  runApp(const ExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'find_ai_chat example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF635BFF),
        useMaterial3: true,
        fontFamily: 'Inter',
        scaffoldBackgroundColor: const Color(0xFFF6F8FA),
      ),
      home: const HomeScreen(),
    );
  }
}

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  @override
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  FindChatMode _mode = FindChatMode.floating;
  FindChatTheme? _theme;
  FindChatVisualStyle? _visualStyle;

  late final FindChatController _controller = FindChatController(
    connectionId: kExampleConnectionId,
  );

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            _buildHeader(),
            Expanded(
              child: _mode == FindChatMode.embedded
                  ? _buildEmbeddedChat()
                  : _buildPlayground(),
            ),
          ],
        ),
      ),
      floatingActionButton: _mode == FindChatMode.embedded
          ? null
          : FindChatWidget(
              connectionId: kExampleConnectionId,
              controller: _controller,
              mode: _mode,
              theme: _theme,
              visualStyle: _visualStyle,
            ),
    );
  }

  Widget _buildHeader() {
    return Container(
      padding: const EdgeInsets.fromLTRB(24, 20, 24, 16),
      decoration: const BoxDecoration(
        color: Colors.white,
        border: Border(bottom: BorderSide(color: Color(0xFFE3E8EF))),
      ),
      child: Row(
        children: [
          Container(
            width: 32,
            height: 32,
            decoration: BoxDecoration(
              gradient: const LinearGradient(
                colors: [Color(0xFF635BFF), Color(0xFF8B5CF6)],
              ),
              borderRadius: BorderRadius.circular(8),
            ),
            padding: const EdgeInsets.all(4),
            child: Image.asset('assets/find_logo.png', fit: BoxFit.contain),
          ),
          const SizedBox(width: 12),
          const Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'find_ai_chat',
                style: TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                  color: Color(0xFF0A2540),
                  letterSpacing: -0.3,
                ),
              ),
              Text(
                'Interactive SDK playground',
                style: TextStyle(
                  fontSize: 12,
                  color: Color(0xFF596780),
                ),
              ),
            ],
          ),
          const Spacer(),
          _buildPill('v0.1.0'),
        ],
      ),
    );
  }

  Widget _buildPill(String label) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
      decoration: BoxDecoration(
        color: const Color(0xFFF0EEFF),
        borderRadius: BorderRadius.circular(20),
      ),
      child: Text(
        label,
        style: const TextStyle(
          fontSize: 11,
          fontWeight: FontWeight.w500,
          color: Color(0xFF635BFF),
        ),
      ),
    );
  }

  Widget _buildPlayground() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _buildSection(
            title: 'Display Mode',
            child: _buildChipGroup<FindChatMode>(
              options: {
                FindChatMode.floating: ('Floating', Icons.bubble_chart_outlined),
                FindChatMode.drawer: ('Drawer', Icons.menu_open_rounded),
                FindChatMode.embedded: ('Embedded', Icons.dashboard_outlined),
              },
              selected: _mode,
              onSelected: (v) => setState(() => _mode = v),
            ),
          ),
          const SizedBox(height: 20),
          _buildSection(
            title: 'Theme',
            child: _buildChipGroup<FindChatTheme?>(
              options: {
                null: ('From config', Icons.settings_outlined),
                FindChatTheme.light: ('Light', Icons.light_mode_outlined),
                FindChatTheme.dark: ('Dark', Icons.dark_mode_outlined),
              },
              selected: _theme,
              onSelected: (v) => setState(() => _theme = v),
            ),
          ),
          const SizedBox(height: 20),
          _buildSection(
            title: 'Visual Style',
            child: _buildChipGroup<FindChatVisualStyle?>(
              options: {
                null: ('From config', Icons.settings_outlined),
                FindChatVisualStyle.classic: ('Classic', Icons.chat_outlined),
                FindChatVisualStyle.gptStyle: ('GPT style', Icons.auto_awesome_outlined),
              },
              selected: _visualStyle,
              onSelected: (v) => setState(() => _visualStyle = v),
            ),
          ),
          const SizedBox(height: 28),
          _buildSection(
            title: 'Programmatic Control',
            subtitle: 'Call these methods from anywhere in your app',
            child: _buildControlButtons(),
          ),
          const SizedBox(height: 28),
          _buildHintCard(),
        ],
      ),
    );
  }

  Widget _buildSection({
    required String title,
    String? subtitle,
    required Widget child,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title.toUpperCase(),
          style: const TextStyle(
            fontSize: 11,
            fontWeight: FontWeight.w600,
            color: Color(0xFF596780),
            letterSpacing: 0.8,
          ),
        ),
        if (subtitle != null) ...[
          const SizedBox(height: 2),
          Text(
            subtitle,
            style: const TextStyle(fontSize: 12, color: Color(0xFF8792A2)),
          ),
        ],
        const SizedBox(height: 10),
        child,
      ],
    );
  }

  Widget _buildChipGroup<T>({
    required Map<T, (String, IconData)> options,
    required T selected,
    required ValueChanged<T> onSelected,
  }) {
    return Wrap(
      spacing: 8,
      runSpacing: 8,
      children: options.entries.map((e) {
        final isActive = e.key == selected;
        return GestureDetector(
          onTap: () => onSelected(e.key),
          child: AnimatedContainer(
            duration: const Duration(milliseconds: 180),
            padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
            decoration: BoxDecoration(
              color: isActive ? const Color(0xFF635BFF) : Colors.white,
              borderRadius: BorderRadius.circular(10),
              border: Border.all(
                color: isActive
                    ? const Color(0xFF635BFF)
                    : const Color(0xFFE3E8EF),
              ),
              boxShadow: isActive
                  ? [
                      BoxShadow(
                        color: const Color(0xFF635BFF).withValues(alpha: 0.25),
                        blurRadius: 8,
                        offset: const Offset(0, 2),
                      ),
                    ]
                  : [
                      BoxShadow(
                        color: Colors.black.withValues(alpha: 0.04),
                        blurRadius: 4,
                        offset: const Offset(0, 1),
                      ),
                    ],
            ),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(
                  e.value.$2,
                  size: 16,
                  color: isActive ? Colors.white : const Color(0xFF596780),
                ),
                const SizedBox(width: 6),
                Text(
                  e.value.$1,
                  style: TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w500,
                    color: isActive ? Colors.white : const Color(0xFF0A2540),
                  ),
                ),
              ],
            ),
          ),
        );
      }).toList(),
    );
  }

  Widget _buildControlButtons() {
    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0xFFE3E8EF)),
      ),
      child: Wrap(
        spacing: 8,
        runSpacing: 8,
        children: [
          _buildActionBtn('open()', Icons.open_in_new_rounded, _controller.open),
          _buildActionBtn('close()', Icons.close_rounded, _controller.close),
          _buildActionBtn('toggle()', Icons.swap_vert_rounded, _controller.toggle),
          _buildActionBtn(
            'sendMessage()',
            Icons.send_rounded,
            () => _controller.sendMessage('Hello from the example app'),
            outlined: true,
          ),
          _buildActionBtn(
            'clearConversation()',
            Icons.delete_outline_rounded,
            _controller.clearConversation,
            outlined: true,
          ),
        ],
      ),
    );
  }

  Widget _buildActionBtn(
    String label,
    IconData icon,
    VoidCallback onPressed, {
    bool outlined = false,
  }) {
    return Material(
      color: outlined ? Colors.transparent : const Color(0xFF0A2540),
      borderRadius: BorderRadius.circular(8),
      child: InkWell(
        onTap: onPressed,
        borderRadius: BorderRadius.circular(8),
        child: Container(
          padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
          decoration: outlined
              ? BoxDecoration(
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: const Color(0xFFE3E8EF)),
                )
              : null,
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              Icon(
                icon,
                size: 14,
                color: outlined ? const Color(0xFF596780) : Colors.white,
              ),
              const SizedBox(width: 6),
              Text(
                label,
                style: TextStyle(
                  fontSize: 12,
                  fontWeight: FontWeight.w500,
                  fontFamily: 'monospace',
                  color: outlined ? const Color(0xFF0A2540) : Colors.white,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildHintCard() {
    final hint = _mode == FindChatMode.floating
        ? 'Tap the floating bubble in the bottom-right corner to open the chat.'
        : _mode == FindChatMode.drawer
            ? 'Use the open() button above or controller.open() to reveal the drawer panel.'
            : 'Switch to Embedded mode to see the chat inline below.';

    return Container(
      padding: const EdgeInsets.all(14),
      decoration: BoxDecoration(
        color: const Color(0xFFFFF8E1),
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: const Color(0xFFFFE082)),
      ),
      child: Row(
        children: [
          const Icon(Icons.lightbulb_outline_rounded, size: 16, color: Color(0xFFF9A825)),
          const SizedBox(width: 10),
          Expanded(
            child: Text(
              hint,
              style: const TextStyle(fontSize: 12, color: Color(0xFF5D4037)),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildEmbeddedChat() {
    return Padding(
      padding: const EdgeInsets.all(24),
      child: Container(
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(16),
          border: Border.all(color: const Color(0xFFE3E8EF)),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.06),
              blurRadius: 16,
              offset: const Offset(0, 4),
            ),
          ],
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(16),
          child: FindChatWidget(
            connectionId: kExampleConnectionId,
            controller: _controller,
            mode: FindChatMode.embedded,
            theme: _theme,
            visualStyle: _visualStyle,
          ),
        ),
      ),
    );
  }
}
2
likes
160
points
225
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Flutter SDK for the Find AI chat assistant. Drop-in widget with floating, drawer and embedded modes, SSE streaming, and signed-mode visitor auth.

Homepage
Repository (GitHub)

License

MIT (license)

Dependencies

flutter, flutter_markdown_plus, http, shared_preferences

More

Packages that depend on find_ai_chat