flutter_agentic β€” Flutter AI Agent SDK

pub package pub points likes Platform License: MIT

The Flutter AI agent SDK β€” build tool-calling AI agents, multi-step LLM pipelines, and on-device / cloud hybrid apps in Flutter.

flutter_agentic gives Flutter developers a single, clean API across 7 AI providers β€” Gemini, OpenAI, Anthropic, HuggingFace, Ollama, on-device Gemma, and GGUF (llama.cpp). ReAct reasoning loop, per-call routing, observable multi-step flows, persistent memory, and a full safety layer β€” all Flutter-first, no backend required.

πŸ‘‰ View on pub.dev Β· GitHub Β· PLATFORM_SETUP.md


Why use this Flutter AI SDK?

  • One API, every provider β€” swap Gemini for Ollama for on-device Gemma with one line
  • True ReAct agent loop β€” the agent reasons, calls tools, observes results, and repeats
  • Per-call routing β€” PolicyRouter routes each request cloud/on-device by your rules
  • Multi-step flows β€” GenesisFlow chains LLM calls into named, observable pipelines
  • Production-ready safety β€” PII redaction, rate limiting, input/output guards built-in
  • Full offline support β€” run Gemma or any GGUF model entirely on-device, no internet

Features

Feature Description
🧠 ReAct agent loop Reasons β†’ calls tools β†’ observes β†’ repeats until done
πŸ”Œ 7 AI providers Gemini, OpenAI, Anthropic, HuggingFace (cloud), Ollama (local server), on-device Gemma, on-device GGUF (llama.cpp)
🧭 Per-call routing PolicyRouter β€” route each call cloud/on-device by your own rules (NEW in 0.2.0)
πŸ”— AI flows GenesisFlow β€” chain multi-step AI pipelines with named, observable steps (NEW in 0.2.0)
πŸ› οΈ Built-in tools Calculator, date/time, HTTP fetch, mock weather β€” zero config
πŸ”§ Custom tools Type-safe GenesisTool.define() with auto JSON Schema
πŸ’Ύ Persistent memory HiveMemoryStore β€” history survives app restarts
πŸ” Smart retry Exponential backoff on 429 / 5xx with RetryProvider
🚦 Safety layer Input guard, PII redaction, rate limiter, concurrency limiter
πŸ—ΊοΈ Model registry Pre-configured context windows & costs for 20+ models
πŸ“‘ Streaming Token-by-token streaming for all cloud providers
🌐 Web safe Cloud providers work on web; on-device excluded automatically

Installation

dependencies:
  flutter_agentic: ^1.0.0

Or directly from source:

dependencies:
  flutter_agentic:
    git:
      url: https://github.com/Devanshv17/flutter_agentic

Quick start β€” Flutter AI agent in 10 lines

import 'package:flutter_agentic/flutter_agentic.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await GenesisAI.init(
    providers: {
      'gemini': GeminiProvider(apiKey: 'YOUR_GEMINI_API_KEY'),
    },
    defaultProviderKey: 'gemini',
  );

  final agent = GenesisAgent(
    provider: GenesisAI.defaultProvider,
    systemPrompt: 'You are a helpful Flutter AI assistant.',
    tools: GenesisTools.all,   // calculator + dateTime + httpRequest + mockWeather
  );

  final response = await agent.chat('What is 1337 * 42?');
  print(response.text); // β†’ "The answer is 56154."
}

AI Providers

Gemini (Google)

GeminiProvider(
  apiKey: 'YOUR_KEY',
  model: 'gemini-2.0-flash',   // default
)

Get a free key at aistudio.google.com.

OpenAI

OpenAIProvider(
  apiKey: 'YOUR_KEY',
  model: 'gpt-4o-mini',   // default
)

Anthropic (Claude)

AnthropicProvider(
  apiKey: 'YOUR_KEY',
  model: 'claude-haiku-4-5',   // default β€” fast & cheap
)

Ollama (local server β€” desktop / LAN)

OllamaProvider(model: 'llama3.2')    // localhost:11434

final status = await OllamaProvider(model: 'phi4').checkStatus();
if (!status.isReady) print(status.reason);
  1. Install: ollama.ai
  2. Pull: ollama pull llama3.2
  3. Ollama starts automatically β€” no extra config needed.

On-device Gemma (Android / iOS / macOS / Windows)

import 'package:flutter_agentic/src/providers/gemma_provider.dart';

final path = '/data/user/0/com.example.app/files/gemma-3n.task';
if (await GemmaModelManager.isDownloaded(path)) {
  final provider = GemmaProvider(
    modelId: 'gemma-3n-e2b-it',
    modelPath: path,
    supportImage: true,
  );
}

final url = GemmaModelManager.downloadUrl('gemma-3n-e2b-it');
final sizeMb = GemmaModelManager.approximateSizeMb('gemma-3n-e2b-it'); // 1100

Supported model IDs: gemma-3n-e2b-it, gemma-3n-e4b-it, gemma-3-1b-it, function-gemma-270m, phi-4-mini, qwen3-0.6b, smollm-135m.

GemmaProvider uses dart:ffi β€” import it directly and guard with if (!kIsWeb).

HuggingFace Inference (cloud β€” any HF model, no download)

final provider = HFInferenceProvider(
  modelId: 'Qwen/Qwen2.5-0.5B-Instruct',
  apiToken: 'hf_xxxx',   // free at huggingface.co/settings/tokens
);

final agent = GenesisHub.fromHFCloud(
  modelId: 'Qwen/Qwen2.5-0.5B-Instruct',
  apiToken: 'hf_xxxx',
  systemPrompt: 'You are a helpful assistant.',
);
Backend Best for
featherless (default) Virtually all public HF models
nebius Large / quantised models
together Llama, Mixtral, popular OSS
sambanova Very fast Llama inference
hfNative HF's own GPU fleet

On-device GGUF / llama.cpp (Android / macOS / Windows / Linux)

import 'package:flutter_agentic/src/providers/llama_cpp_provider.dart';

final provider = LlamaCppProvider(
  modelPath: '/path/to/model.gguf',
  nCtx: 2048,
  nThreads: 4,
);

// Or auto-detect via GenesisHub:
final agent = await GenesisHub.fromFile('/path/to/model.gguf');
final reply = await agent.chat('What is the capital of France?');
final modelsDir = await GenesisHub.platformModelsDir();
// Android: /data/user/0/com.example.app/files/genesis_models
// iOS/macOS: <NSApplicationSupport>/genesis_models
// Windows/Linux: <ApplicationSupport>/genesis_models

LlamaCppProvider requires dart:ffi. Import it directly. GGUF on iOS needs llama.cpp as an xcframework β€” see PLATFORM_SETUP.md.


Custom tools

final weatherTool = GenesisTool.define(
  name: 'get_weather',
  description: 'Returns current weather for a city.',
  params: [
    ToolParam.string('city', 'City name, e.g. "Mumbai"'),
    ToolParam.stringEnum('unit', 'Temperature unit', ['celsius', 'fahrenheit']),
  ],
  execute: (args) async {
    final city = args['city'] as String;
    final unit = args['unit'] as String? ?? 'celsius';
    return (await fetchWeather(city, unit)).toJson();
  },
);

final agent = GenesisAgent(
  provider: myProvider,
  tools: [weatherTool, GenesisTools.calculator],
);

ToolParam types

Constructor JSON Schema type
ToolParam.string(name, desc) string
ToolParam.number(name, desc) number (double)
ToolParam.integer(name, desc) integer
ToolParam.boolean(name, desc) boolean
ToolParam.stringEnum(name, desc, values) string + enum
ToolParam.array(name, desc, items) array
ToolParam.object(name, desc, props) object

Persistent memory

await HiveMemoryStore.initialize();

final agent = GenesisAgent(
  provider: myProvider,
  memory: HiveMemoryStore(),
  sessionId: 'user_${userId}',
);

await agent.clearHistory();
final history = await agent.getHistory();

Use InMemoryStore() (default) for ephemeral memory.


Streaming

final agent = GenesisAgent(provider: myProvider);

await for (final chunk in agent.chatStream('Tell me a story.')) {
  stdout.write(chunk);
}

ReAct step callbacks

final response = await agent.chat(
  'What is the weather in Tokyo and what is 100^0.5?',
  onStep: (step) {
    switch (step) {
      case ThinkingStep(:final thought):
        print('πŸ’­ $thought');
      case ToolCallStep(:final toolName, :final arguments):
        print('πŸ”§ calling $toolName($arguments)');
      case ToolResultStep(:final toolName, :final result):
        print('βœ… $toolName β†’ $result');
      case FinalResponseStep(:final text):
        print('🏁 $text');
      case ErrorStep(:final message):
        print('❌ $message');
    }
  },
);

Safety layer

// Input guard β€” blocks empty, too-long, and injected inputs
final guard = InputGuard();
final clean = guard.validate(userInput);
final strict = InputGuard.withInjectionDetection();

// Output guard β€” PII redaction, truncation, blocklists
final safe = OutputGuard.withPiiRedaction();
final text = safe.process(rawLlmText);

// Rate limiter + concurrency cap
final limiter = RateLimiter(maxRequests: 20, windowDuration: Duration(minutes: 1));
limiter.check(sessionId);

final concurrency = ConcurrencyLimiter(maxConcurrent: 3);
final response = await concurrency.run(userId, () => agent.chat(message));

Smart routing & fallback

// Fallback to secondary if primary fails
final router = SmartRouter(
  primary: GeminiProvider(apiKey: '...'),
  secondary: OllamaProvider(model: 'llama3.2'),
);

// Strip PII before sending to the cloud
final privacyRouter = PrivacyRouter(
  cloudProvider: OpenAIProvider(apiKey: '...'),
  sensitiveKeys: ['email', 'phone', 'ssn'],
);

Per-call routing β€” PolicyRouter (NEW in 0.2.0)

SmartRouter picks one backend at init; PolicyRouter re-decides on every call β€” cheap / private / offline-friendly tasks stay on-device, hard reasoning goes to the cloud. Your agent call sites never change:

final router = PolicyRouter(
  providers: {
    'local': GemmaProvider(...),          // private, free, offline
    'cloud': GeminiProvider(apiKey: ''),  // smart, paid
  },
  defaultProvider: 'cloud',
  rules: [
    RouteRules.sensitive(useProvider: 'local'),   // PII stays on-device
    RouteRules.shortInput(useProvider: 'local'),  // small tasks stay local
    RouteRules.needsTools(useProvider: 'cloud'),  // tool calls need cloud
  ],
  onRoute: (d) => print(d), // log / show a "πŸ”’ local" badge in your UI
);

final agent = GenesisAgent(provider: router); // call sites never change

Built-in rules: sensitive(), shortInput(), longContext(), needsTools(), streaming(), custom().

Need a one-off override? Force a provider per call:

await agent.chat('My salary is 95k, plan my budget', provider: localGemma);

GenesisFlow β€” multi-step AI pipelines (NEW in 0.2.0)

Chain agent calls, tool runs, and transforms into one named, type-safe, observable pipeline (inspired by Genkit flows, built Flutter-first):

final tripPlanner = GenesisFlow.start<String>('trip-planner')
    .then<String>('extract-city', (input, ctx) async {
      return (await extractor.chat('Extract the city: $input')).text;
    })
    .then<String>('fetch-weather', (city, ctx) async {
      ctx.set('city', city);
      return await weatherApi.forecast(city);
    })
    .then<String>('write-plan', (weather, ctx) async {
      final city = ctx.get<String>('city');
      return (await planner.chat('Plan a day in $city. Weather: $weather')).text;
    });

final plan = await tripPlanner.run(
  'I want to visit Tokyo tomorrow',
  onEvent: (e) => print(e),  // FlowStepStarted / Completed / Failed β†’ live UI
);

Failures throw FlowException(flowName, stepName, cause) β€” logs point straight at the failing stage.


Retry on failures

final resilient = RetryProvider(
  inner: GeminiProvider(apiKey: '...'),
  maxAttempts: 3,
  initialDelayMs: 500,   // doubles each retry with Β±25% jitter
);

Model registry

final config = ModelRegistry.get('gemini-2.0-flash');
print(config.contextWindow);          // 1048576
print(config.inputCostPer1MTokens);  // 0.075

final manager = ContextManager.forModel('gemini-2.0-flash');
final fitted = manager.fit(messages);

Logging

await GenesisAI.init(
  providers: { ... },
  logLevel: LogLevel.debug,
);

GenesisLogger.setHandler((level, message, [error]) {
  Crashlytics.instance.log('[$level] $message');
});

Default: LogLevel.none β€” silent in production.


Platform support

Platform Cloud (Gemini / OpenAI / Anthropic / HF) Ollama On-device Gemma On-device GGUF
Android βœ… βœ… βœ… βœ…
iOS βœ… βœ… βœ… ⚠️ xcframework needed
macOS βœ… βœ… βœ… βœ…
Windows βœ… βœ… βœ… βœ…
Web βœ… ❌ ❌ ❌
Linux βœ… βœ… ❌ βœ…

See PLATFORM_SETUP.md for native setup instructions per platform.


Roadmap

  • genesis_ai_ui β€” dynamic Flutter UI renderer driven by AI responses (A2UI)
  • genesis_ai_tools β€” device location, camera, clipboard, contacts tools
  • More providers: Mistral, Groq, Cohere, local llama.cpp server
  • Semantic memory with vector search

License

MIT β€” see LICENSE.

Libraries

flutter_agentic
flutter_agentic β€” Build AI agents in Flutter. Local and cloud, one API.