ai_kit 0.1.0
ai_kit: ^0.1.0 copied to clipboard
AI Integration Toolkit for Flutter. Multi-provider support (OpenAI, Gemini, Claude), streaming chat UI, smart widgets, and AI-powered components. Build AI features in minutes.
import 'package:ai_kit/ai_kit.dart';
import 'package:flutter/material.dart';
void main() {
// Initialize AIKit with your provider(s)
// Uncomment and add your API key:
//
// AIKit.init(
// providers: [
// OpenAIProvider(apiKey: 'sk-your-key-here'),
// // GeminiProvider(apiKey: 'AI-your-key-here'),
// // ClaudeProvider(apiKey: 'sk-ant-your-key-here'),
// ],
// );
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AI Kit Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorSchemeSeed: const Color(0xFF6C63FF),
useMaterial3: true,
brightness: Brightness.light,
),
darkTheme: ThemeData(
colorSchemeSeed: const Color(0xFF6C63FF),
useMaterial3: true,
brightness: Brightness.dark,
),
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('AI Kit Examples')),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_ExampleCard(
title: 'Simple Chat',
subtitle: 'Full-featured AI chat with streaming responses',
icon: Icons.chat_bubble_outline,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const SimpleChatExample()),
),
),
const SizedBox(height: 12),
_ExampleCard(
title: 'Multi-Provider Chat',
subtitle: 'Switch between OpenAI, Gemini, and Claude',
icon: Icons.swap_horiz,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const MultiProviderExample()),
),
),
const SizedBox(height: 12),
_ExampleCard(
title: 'Quick Question',
subtitle: 'Simple ask() API - one line AI call',
icon: Icons.bolt,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const QuickAskExample()),
),
),
const SizedBox(height: 12),
_ExampleCard(
title: 'Custom Theme',
subtitle: 'Fully themed chat interface',
icon: Icons.palette,
onTap: () => Navigator.push(
context,
MaterialPageRoute(builder: (_) => const ThemedChatExample()),
),
),
],
),
);
}
}
class _ExampleCard extends StatelessWidget {
final String title;
final String subtitle;
final IconData icon;
final VoidCallback onTap;
const _ExampleCard({
required this.title,
required this.subtitle,
required this.icon,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
child: Icon(icon, color: Theme.of(context).colorScheme.primary),
),
title: Text(title, style: const TextStyle(fontWeight: FontWeight.w600)),
subtitle: Text(subtitle),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
),
);
}
}
// ─── Example 1: Simple Chat ─────────────────────────────────────────
class SimpleChatExample extends StatelessWidget {
const SimpleChatExample({super.key});
@override
Widget build(BuildContext context) {
final isInitialized = AIKit.isInitialized;
return Scaffold(
appBar: AppBar(title: const Text('Simple Chat')),
body: isInitialized
? const AIChatView(
systemPrompt:
'You are a friendly and helpful AI assistant. '
'Keep your responses concise and useful.',
suggestions: [
'Tell me a joke',
'Explain quantum computing simply',
'Write a haiku about Flutter',
],
showUsage: true,
)
: const _NotInitializedView(),
);
}
}
// ─── Example 2: Multi-Provider ──────────────────────────────────────
class MultiProviderExample extends StatefulWidget {
const MultiProviderExample({super.key});
@override
State<MultiProviderExample> createState() => _MultiProviderExampleState();
}
class _MultiProviderExampleState extends State<MultiProviderExample> {
String? _selectedProvider;
@override
Widget build(BuildContext context) {
if (!AIKit.isInitialized) {
return Scaffold(
appBar: AppBar(title: const Text('Multi-Provider Chat')),
body: const _NotInitializedView(),
);
}
final providers = AIKit.instance.providerNames;
_selectedProvider ??= providers.isNotEmpty ? providers.first : null;
return Scaffold(
appBar: AppBar(
title: const Text('Multi-Provider Chat'),
actions: [
if (providers.length > 1)
PopupMenuButton<String>(
icon: const Icon(Icons.swap_horiz),
onSelected: (value) {
setState(() => _selectedProvider = value);
},
itemBuilder: (context) => providers
.map(
(p) => PopupMenuItem(
value: p,
child: Row(
children: [
if (p == _selectedProvider)
const Icon(Icons.check, size: 18),
if (p != _selectedProvider) const SizedBox(width: 18),
const SizedBox(width: 8),
Text(p),
],
),
),
)
.toList(),
),
],
),
body: AIChatView(
key: ValueKey(_selectedProvider),
providerName: _selectedProvider,
systemPrompt:
'You are a helpful assistant. '
'Mention that you are powered by $_selectedProvider.',
showUsage: true,
showCost: true,
),
);
}
}
// ─── Example 3: Quick Ask ───────────────────────────────────────────
class QuickAskExample extends StatefulWidget {
const QuickAskExample({super.key});
@override
State<QuickAskExample> createState() => _QuickAskExampleState();
}
class _QuickAskExampleState extends State<QuickAskExample> {
final _controller = TextEditingController();
String? _answer;
bool _isLoading = false;
String? _error;
Future<void> _ask() async {
if (_controller.text.trim().isEmpty) return;
setState(() {
_isLoading = true;
_error = null;
_answer = null;
});
try {
final answer = await AIKit.instance.ask(_controller.text);
setState(() => _answer = answer);
} catch (e) {
setState(() => _error = e.toString());
} finally {
setState(() => _isLoading = false);
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!AIKit.isInitialized) {
return Scaffold(
appBar: AppBar(title: const Text('Quick Ask')),
body: const _NotInitializedView(),
);
}
return Scaffold(
appBar: AppBar(title: const Text('Quick Ask')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'One line of code:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(8),
),
child: const Text(
"final answer = await AIKit.instance.ask('...');",
style: TextStyle(fontFamily: 'monospace', fontSize: 13),
),
),
const SizedBox(height: 24),
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Ask anything...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
suffixIcon: IconButton(
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.send),
onPressed: _isLoading ? null : _ask,
),
),
onSubmitted: (_) => _ask(),
),
const SizedBox(height: 16),
if (_answer != null)
Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: SingleChildScrollView(
child: SelectableText(_answer!),
),
),
),
),
if (_error != null)
Card(
color: Theme.of(context).colorScheme.errorContainer,
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(
_error!,
style: TextStyle(
color: Theme.of(context).colorScheme.onErrorContainer,
),
),
),
),
],
),
),
);
}
}
// ─── Example 4: Themed Chat ─────────────────────────────────────────
class ThemedChatExample extends StatelessWidget {
const ThemedChatExample({super.key});
@override
Widget build(BuildContext context) {
if (!AIKit.isInitialized) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Theme')),
body: const _NotInitializedView(),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('Themed Chat'),
backgroundColor: const Color(0xFF1A1A2E),
foregroundColor: Colors.white,
),
body: AIChatView(
systemPrompt: 'You are a creative writing assistant.',
theme: const AIChatTheme(
primaryColor: Color(0xFFE94560),
backgroundColor: Color(0xFF1A1A2E),
surfaceColor: Color(0xFF16213E),
userBubbleColor: Color(0xFFE94560),
aiBubbleColor: Color(0xFF16213E),
userTextColor: Colors.white,
aiTextColor: Color(0xFFEEEEEE),
textColor: Colors.white,
secondaryTextColor: Colors.white54,
inputBackgroundColor: Color(0xFF0F3460),
inputBorderColor: Color(0xFF533483),
errorColor: Color(0xFFFF6B6B),
showTimestamp: true,
),
suggestions: [
'Write a short story opening',
'Create a character description',
'Describe a fantasy world',
],
),
);
}
}
// ─── Helper: Not Initialized View ───────────────────────────────────
class _NotInitializedView extends StatelessWidget {
const _NotInitializedView();
@override
Widget build(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.warning_amber_rounded,
size: 64,
color: Theme.of(context).colorScheme.error,
),
const SizedBox(height: 16),
Text(
'AIKit Not Initialized',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
const Text(
'Open main.dart and uncomment the AIKit.init() call '
'with your API key to try this example.',
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
),
child: const SelectableText(
"AIKit.init(\n"
" providers: [\n"
" OpenAIProvider(apiKey: 'sk-...'),\n"
" ],\n"
");",
style: TextStyle(fontFamily: 'monospace', fontSize: 13),
),
),
],
),
),
);
}
}