Genkit Agents API for Dart: Full-Stack Conversational AI in Flutter
How the Genkit Agents API brings full-stack conversational AI to Dart and Flutter — defining agents on the server and driving them from Flutter with a shared chat() interface.
Published on • September 18, 2026
AI Assistant

Building a conversational AI feature — a support assistant that remembers context, a copilot that works across turns, a chatbot with tool calling — requires wiring up message history, the tool loop, streaming, persistence, and a client protocol by hand. That plumbing repeats on every project and has little to do with what makes your app distinct.
The Genkit Agents API, now available in Dart (announced July 2026), packages all of that behind one interface. You define an agent on the server, then drive it with the same chat() API whether it runs in-process or behind an HTTP endpoint.
What the Agents API Solves
A single generate() call handles one request-response. Real conversational AI needs:
- Message history — Track multi-turn conversations
- Tool execution loop — Let the model call tools and process results
- Streaming — Deliver tokens as they’re generated
- Persistence — Save and restore conversation state
- Client protocol — Frontend and backend speak the same wire format
The Agents API handles all of this. You define the agent once, and it manages everything else.
Defining an Agent
An agent needs a name, a system prompt, and optionally tools and a session store:
import 'package:genkit/genkit.dart';
import 'package:genkit/io.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart';
final ai = Genkit(
plugins: [googleAI()],
model: googleAI.gemini('gemini-flash-latest'),
);
final getWeather = ai.defineTool(
name: 'getWeather',
description: 'Get the current weather for a given location.',
inputSchema: GetWeatherInput.$schema,
outputSchema: GetWeatherOutput.$schema,
fn: (input, _) async => GetWeatherOutput(
weather: 'Sunny in ${input.location}',
temperature: '71F',
),
);
final weatherAgent = ai.defineAgent(
name: 'weatherAgent',
system: 'You are an assistant helping with weather information. '
'Use the getWeather tool.',
tools: [getWeather],
store: FileSessionStore('.sessions'),
);
The same agent object handles one-shot replies, streamed turns, paused tool calls, and multi-turn conversations.
Driving the Agent
In-Process (Server-Side)
Call the agent directly from Dart code:
final chat = weatherAgent.chat();
final turn = chat.sendStream(text: 'What is the weather in London?');
await for (final chunk in turn.stream) {
stdout.write(chunk.text);
}
final res = await turn.response;
A chat carries its snapshotId and state forward across turns automatically. Multi-turn is just calling send again.
From Flutter via HTTP
Serve the agent over HTTP, then call it from your Flutter app:
// Server: serve agent routes
// agent.shelf.dart
import 'package:genkit/shelf.dart';
final router = weatherAgent.serveShelf();
// Client: Flutter app
import 'package:genkit/beta/client.dart';
const agent = remoteAgent<WeatherState>(
url: 'http://localhost:8080/api/weatherAgent',
);
final chat = agent.chat();
final res = await chat.send('Weather in Tokyo?');
The Flutter client uses the same chat() interface whether the agent runs locally or over HTTP. And because the endpoint speaks a shared wire protocol, a Flutter app can talk to agents written in Dart, JavaScript, Go, or Python — without changing a line of client code.
Session Persistence
Add a store to make the agent server-managed. The server persists messages, custom state, and artifacts as snapshots. Clients continue by sending back a session ID.
final agent = ai.defineAgent(
name: 'assistant',
system: 'You are a helpful assistant.',
store: FileSessionStore('.sessions'),
);
Available store implementations:
FileSessionStore— File-based persistence for developmentMemorySessionStore— In-memory (no persistence)- Custom stores — Implement
SessionStorefor your database
Custom State
Tools can mutate typed session state through ai.currentSession(), and Genkit streams the changes to the client as customPatch chunks. This is useful for task lists, workflow status, or any structured value that drives the next turn:
final updateTask = ai.defineTool(
name: 'updateTask',
description: 'Mark a task as complete.',
inputSchema: UpdateTaskInput.$schema,
outputSchema: UpdateTaskOutput.$schema,
fn: (input, context) async {
final session = context.currentSession;
session.state.tasks[input.taskId].completed = true;
return UpdateTaskOutput(success: true);
},
);
Human in the Loop
The Agents API supports interrupts — pausing execution when a tool call requires human approval before proceeding:
final approveOrder = ai.defineTool(
name: 'approveOrder',
description: 'Place an order. Requires human approval.',
inputSchema: OrderInput.$schema,
outputSchema: OrderOutput.$schema,
interrupt: true, // Pauses for human approval
fn: (input, _) async => placeOrder(input),
);
When the agent hits an interrupt, it pauses and returns the pending tool call to the client. The UI shows an approval dialog, and execution resumes when the user confirms.
Streaming
Stream agent responses token-by-token for responsive UIs:
final chat = weatherAgent.chat();
final turn = chat.sendStream(text: 'Tell me about the weather');
await for (final chunk in turn.stream) {
if (chunk.text.isNotEmpty) {
setState(() => _response += chunk.text);
}
}
The stream delivers text chunks, state patches, and tool call events as they happen.
When to Use Agents vs Flows
| Scenario | Use |
|---|---|
| Single request-response | Flow or generate() |
| Scheduled batch jobs | Flow |
| Conversational chat | Agent |
| Multi-turn refinement | Agent |
| Approval workflows | Agent |
| Stateful copilots | Agent |
| Maximum architectural control | Flow |
Agents are the right abstraction when your feature is naturally conversational or iterative. For everything else, flows and generate() give you more control.
Getting Started
Install the required packages:
dependencies:
genkit: ^0.13.0
genkit_google_genai: ^0.3.0
The Agents API is available from the genkit/beta import:
import 'package:genkit/beta.dart';
Define an agent, add tools and a store, and start chatting.
Conclusion
The Genkit Agents API turns the repeated plumbing of conversational, full-stack AI into something you configure rather than rebuild. Define an agent on the server, give it a store when you want persistence, and drive it from your Flutter app with the same chat() interface. Whether your agent runs locally, on a server, or behind an HTTP endpoint — and whether it’s written in Dart, JS, Go, or Python — the client code stays the same.
References:
- Genkit Agents for Dart — genkit.dev
- Full-stack Agents Documentation — genkit.dev
- Build Agentic Full-Stack Apps with Genkit — developers.googleblog.com
- Genkit Agents API InfoQ Report — infoq.com