agent_kit 0.1.2
agent_kit: ^0.1.2 copied to clipboard
Drop-in Flutter widgets for AI agent UIs — tool-call cards, thinking bubbles, citation chips, plan-progress timelines. Backend-agnostic. shadcn-for-agents.
example/lib/main.dart
import 'dart:async';
import 'package:agent_kit/agent_kit.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const AgentKitDemoApp());
}
class AgentKitDemoApp extends StatelessWidget {
const AgentKitDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'agent_kit demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,
),
),
home: const DemoHome(),
);
}
}
class DemoHome extends StatefulWidget {
const DemoHome({super.key});
@override
State<DemoHome> createState() => _DemoHomeState();
}
class _DemoHomeState extends State<DemoHome> {
late StreamController<AgentEvent> _controller;
int _runId = 0;
@override
void initState() {
super.initState();
_controller = StreamController<AgentEvent>.broadcast();
_replay();
}
@override
void dispose() {
_controller.close();
super.dispose();
}
Future<void> _replay() async {
final int run = ++_runId;
Future<void> emit(AgentEvent e, int ms) async {
await Future<void>.delayed(Duration(milliseconds: ms));
if (!mounted || run != _runId || _controller.isClosed) return;
_controller.add(e);
}
await emit(
const PlanEvent(<PlanStep>[
PlanStep(
id: 'p1',
label: 'Understand the question',
state: PlanStepState.running,
),
PlanStep(id: 'p2', label: 'Search the web'),
PlanStep(id: 'p3', label: 'Synthesize answer'),
]),
200,
);
await emit(
const ThinkingEvent(
ThinkingStep(
text:
'User wants a comparison of Flutter vs React Native in 2026. '
'I should highlight perf, hiring market, and platform parity.',
isStreaming: true,
),
),
400,
);
await emit(
const ThinkingEvent(
ThinkingStep(
text:
'User wants a comparison of Flutter vs React Native in 2026. '
'I should highlight perf, hiring market, and platform parity.',
),
),
600,
);
await emit(
const ToolCallEvent(
ToolCall(
id: 'tc-1',
name: 'search_web',
args: <String, Object?>{'query': 'Flutter vs React Native 2026'},
state: ToolCallState.running,
),
),
500,
);
await emit(
const ToolCallEvent(
ToolCall(
id: 'tc-1',
name: 'search_web',
args: <String, Object?>{'query': 'Flutter vs React Native 2026'},
state: ToolCallState.success,
result: <String, Object?>{
'hits': 12,
'top': <String>['flutter.dev', 'reactnative.dev'],
},
durationMs: 842,
),
),
1200,
);
await emit(
const PlanEvent(<PlanStep>[
PlanStep(
id: 'p1',
label: 'Understand the question',
state: PlanStepState.done,
),
PlanStep(
id: 'p2',
label: 'Search the web',
state: PlanStepState.done,
),
PlanStep(
id: 'p3',
label: 'Synthesize answer',
state: PlanStepState.running,
),
]),
200,
);
await emit(
const CitationEvent(<Citation>[
Citation(
sourceId: 'c1',
title: 'Flutter performance overview',
url: 'https://flutter.dev/perf',
snippet:
'Impeller pipeline ships on iOS and Android by default in 2026, delivering 60fps on the median device.',
),
Citation(
sourceId: 'c2',
title: 'React Native 2026 release notes',
url: 'https://reactnative.dev/blog',
snippet:
'The New Architecture is now mandatory; Hermes is the default JS engine.',
),
Citation(
sourceId: 'c3',
title: 'Stack Overflow 2026 survey',
url: 'https://insights.stackoverflow.com/survey/2026',
snippet:
'Mobile developer hiring favors Flutter 1.4x over React Native in EU markets.',
),
]),
400,
);
await emit(
const PlanEvent(<PlanStep>[
PlanStep(
id: 'p1',
label: 'Understand the question',
state: PlanStepState.done,
),
PlanStep(
id: 'p2',
label: 'Search the web',
state: PlanStepState.done,
),
PlanStep(
id: 'p3',
label: 'Synthesize answer',
state: PlanStepState.done,
),
]),
600,
);
}
@override
Widget build(BuildContext context) {
final AgentKitTheme theme = AgentKitTheme.fromColorScheme(
Theme.of(context).colorScheme,
);
return Scaffold(
backgroundColor: theme.background,
appBar: AppBar(
title: const Text('agent_kit'),
elevation: 0,
actions: <Widget>[
IconButton(
tooltip: 'Replay',
onPressed: _replay,
icon: const Icon(Icons.replay),
),
],
),
body: AgentKitThemeScope(
theme: theme,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 720),
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: AgentView(stream: _controller.stream),
),
),
),
),
);
}
}