chat_pilot_kit_flutter 1.0.0-beta.5 copy "chat_pilot_kit_flutter: ^1.0.0-beta.5" to clipboard
chat_pilot_kit_flutter: ^1.0.0-beta.5 copied to clipboard

Flutter widgets and Riverpod integration for chat_pilot_kit_dart.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:chat_pilot_kit_flutter/chat_pilot_kit_flutter.dart';

import 'app_extensions.dart';
import 'pages/chat_page.dart';
import 'pages/demos_page.dart';
import 'pages/settings_page.dart';
import 'services/mock_agent_service.dart';

void main() {
  final controller = createChatPilotKit(
    agentService: MockAgentService(),
    overrideExtensions: extensions,
  );

  runApp(
    ChatPilotKitScope(
      controller: controller,
      child: const PlaygroundApp(),
    ),
  );
}

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

  @override
  State<PlaygroundApp> createState() => _PlaygroundAppState();
}

class _PlaygroundAppState extends State<PlaygroundApp> {
  ThemeMode _themeMode = ThemeMode.system;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Chat Pilot Kit Playground',
      themeMode: _themeMode,
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF3B82F6),
        useMaterial3: true,
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        colorSchemeSeed: const Color(0xFF3B82F6),
        useMaterial3: true,
        brightness: Brightness.dark,
      ),
      home: PlaygroundHome(
        themeMode: _themeMode,
        onThemeModeChanged: (mode) {
          setState(() => _themeMode = mode);
        },
      ),
    );
  }
}

class PlaygroundHome extends ConsumerStatefulWidget {
  final ThemeMode themeMode;
  final ValueChanged<ThemeMode> onThemeModeChanged;

  const PlaygroundHome({
    super.key,
    required this.themeMode,
    required this.onThemeModeChanged,
  });

  @override
  ConsumerState<PlaygroundHome> createState() => _PlaygroundHomeState();
}

class _PlaygroundHomeState extends ConsumerState<PlaygroundHome> {
  int _tabIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_titleForTab(_tabIndex)),
        centerTitle: false,
      ),
      body: IndexedStack(
        index: _tabIndex,
        children: [
          const ChatPage(),
          DemosPage(onDemoTriggered: () => setState(() => _tabIndex = 0)),
          SettingsPage(
            themeMode: widget.themeMode,
            onThemeModeChanged: widget.onThemeModeChanged,
            onShowGallery: _showGallery,
          ),
        ],
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _tabIndex,
        onDestinationSelected: (i) => setState(() => _tabIndex = i),
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.chat_bubble_outline),
            selectedIcon: Icon(Icons.chat_bubble),
            label: 'Chat',
          ),
          NavigationDestination(
            icon: Icon(Icons.science_outlined),
            selectedIcon: Icon(Icons.science),
            label: 'Demos',
          ),
          NavigationDestination(
            icon: Icon(Icons.settings_outlined),
            selectedIcon: Icon(Icons.settings),
            label: 'Settings',
          ),
        ],
      ),
    );
  }

  String _titleForTab(int index) {
    return switch (index) {
      0 => 'Chat Pilot Kit',
      1 => 'Demos',
      2 => 'Settings',
      _ => 'Chat Pilot Kit',
    };
  }

  void _showGallery() {
    final controller = ref.read(chatPilotKitControllerProvider);

    controller.importConversations(
      const [
        ConversationBeanInput(
          role: ConversationRole.client,
          nodes: [
            ConversationNodeInput(type: 'text', content: 'Show me every NodeView.'),
          ],
        ),
        ConversationBeanInput(
          role: ConversationRole.aiWorker,
          nodes: [
            ConversationNodeInput(
              type: 'markdown',
              content:
                  '# NodeView Gallery\n\nAll built-in node types rendered below.\n\n'
                  '- **Text**, **Markdown**, **Thinking**, **ToolCall**\n'
                  '- **Image**, **Audio**, **Video**, **File**\n\n'
                  '```dart\n'
                  'final kit = createChatPilotKit(agentService: service);\n'
                  '```\n',
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'thinking',
              content: {
                'text': 'This is a collapsed thinking block example.',
                'collapsed': false,
              },
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'tool_call',
              content: {
                'name': 'get_weather',
                'arguments': '{"city": "Beijing"}',
                'status': 'completed',
                'result': '{"temperature": "22C"}',
              },
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'image',
              content: {
                'url': 'https://picsum.photos/seed/gallery/480/320',
                'alt': 'Random photo',
              },
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'audio',
              content: {
                'url': '#',
                'mimeType': 'audio/mpeg',
                'duration': 84.0,
              },
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'video',
              content: {
                'url': '#',
                'poster': 'https://picsum.photos/seed/gvid/480/270',
                'duration': 150.0,
              },
            ),
            ConversationNodeInput<Map<String, dynamic>>(
              type: 'file',
              content: {
                'url': '#',
                'fileName': 'readme.md',
                'fileSize': 1234,
                'fileType': 'text/markdown',
              },
            ),
          ],
        ),
      ],
      options: const ImportOptions(position: ImportPosition.replace),
    );

    setState(() => _tabIndex = 0);
  }
}