Full-Stack Agentic Dart: Building End-to-End Typed AI Agents with Genkit Dart & Shelf
1. Context & The "Why": Why Client-Embedded Cloud LLM Calls Fail in Production
When mobile teams prototype their first cloud-powered AI feature in Flutter, the most common shortcut is calling the Gemini REST SDK directly from the mobile binary. While convenient for a hackathon, shipping direct cloud LLM calls inside a production APK or IPA creates three critical engineering failures:
- Credential Extraction & Billing Abuse: Any API key compiled into a mobile binary or passed via
--dart-definecan be extracted via static decompilation (`strings`, `jadx`) or TLS proxy inspection (`mitmproxy`), exposing your cloud billing quota to unlimited abuse. - Prompt Tampering & Bypassed Business Rules: If the mobile app constructs the system prompt or executes tool callbacks locally, a modified client can spoof tool outputs, bypass regional order quotas, or inject unauthorized discounts.
- Schema Drift Between Backend & Mobile: Historically, moving AI orchestration to the backend forced Flutter teams to maintain a Python or Node.js microservice, duplicating JSON serialization logic and breaking compile-time type safety across the wire.
Genkit Dart eliminates this trade-off. You write your AI flows, server-side tools, human-in-the-loop guardrails, and HTTP endpoints in 100% pure Dart on the server, and share the exact same schemantic data contracts with your Flutter client.
1. Contexte & Pourquoi : L'échec des appels LLM Cloud embarqués côté client
Lorsqu'une équipe mobile prototype sa première fonctionnalité IA Cloud dans Flutter, le raccourci le plus fréquent consiste à appeler l'API Gemini directement depuis le binaire mobile. Bien que pratique pour une démonstration rapide, embarquer des appels LLM Cloud directement dans un APK ou IPA de production entraîne trois failles majeures :
- Extraction de clés API & Abus de facturation : Toute clé API compilée dans un binaire mobile ou injectée via
--dart-definepeut être extraite par décompilation statique (`strings`, `jadx`) ou interception réseau (`mitmproxy`), exposant votre quota Cloud à des abus illimités. - Altération des Prompts & Contournement des règles métier : Si l'application mobile construit le prompt système ou exécute les outils localement, un client modifié peut falsifier les réponses d'outils, contourner les quotas régionaux ou s'attribuer des remises non autorisées.
- Divergence de schémas entre Backend et Mobile : Historiquement, déporter l'orchestration IA côté serveur obligeait les équipes Flutter à maintenir un microservice Python ou Node.js, dupliquant la sérialisation JSON et brisant le typage fort de bout en bout.
Genkit Dart résout définitivement ce dilemme. Vous développez vos flows IA, vos outils serveur, vos garde-fous avec validation humaine et vos endpoints HTTP en 100% pur Dart côté serveur, tout en partageant exactement les mêmes contrats schemantic avec votre client Flutter.
With schemantic + genkit + genkit_shelf on the backend and package:genkit/client.dart in Flutter, a single @Schema() definition validates LLM structured outputs on the server and deserializes streaming responses on the phone.
Avec schemantic + genkit + genkit_shelf côté serveur et package:genkit/client.dart dans Flutter, une seule définition @Schema() valide la sortie structurée du LLM sur le serveur et désérialise les flux en temps réel sur le téléphone.
2. Learning Goals & What You Will Build
In this 45-minute engineering codelab, you will build a complete B2B supply-chain compliance & order audit workspace (agentic_commerce_workspace) organized as a pure Dart & Flutter monorepo:
- Shared Typed Contracts (
packages/shared_contracts): Define compile-time and runtime JSON schemas usingschemantic($OrderAuditInput,$OrderAuditResult,$InventoryLookupInput,$PartnerDiscountInput) with field boundary constraints. - Genkit Dart Server & Structured Output Flows (
packages/agent_server): ConfigureGenkit(plugins: [googleAI()])withgemini-2.5-flashand enforce strict JSON schema conformance viaoutputSchema. - Authoritative Server-Side Tools: Register
verifyInventoryAndQuotaandapplyPartnerDiscountviaai.defineToolso prices, stock levels, and regional quotas are resolved strictly on the server. - Human-in-the-Loop Guardrails (
genkit_middleware): Intercept sensitive state-mutating tool calls withToolApprovalPlugin(), handleFinishReason.interrupted, and resume generation safely viainterruptRestart. - Real-Time Streaming & Shelf HTTP Server (
genkit_shelf): Stream live audit progress chunks viacontext.sendChunk()while returning a final strongly typedOrderAuditResult, mounted onto a customshelf_routerpipeline. - Flutter Streaming Client (
apps/flutter_client): Consume both unary and streaming remote flows usingdefineRemoteActionfrompackage:genkit/client.dart.
2. Objectifs & Ce que vous allez construire
Au cours de ce codelab technique de 45 minutes, vous allez construire un système complet d'audit et de conformité de commandes B2B (agentic_commerce_workspace) structuré en monorepo Dart & Flutter :
- Contrats Typés Partagés (
packages/shared_contracts) : Définir des schémas JSON validés à la compilation et à l'exécution avecschemantic($OrderAuditInput,$OrderAuditResult,$InventoryLookupInput,$PartnerDiscountInput). - Serveur Genkit Dart & Flows à Sortie Structurée (
packages/agent_server) : InitialiserGenkit(plugins: [googleAI()])avecgemini-2.5-flashet imposer un schéma de sortie strict viaoutputSchema. - Outils Serveur Autoritatifs : Enregistrer
verifyInventoryAndQuotaetapplyPartnerDiscountavecai.defineToolafin que les prix, les stocks et les quotas régionaux soient vérifiés exclusivement côté serveur. - Garde-fous Human-in-the-Loop (
genkit_middleware) : Intercepter les appels d'outils sensibles avecToolApprovalPlugin(), gérer l'étatFinishReason.interruptedet reprendre l'exécution après approbation viainterruptRestart. - Streaming Temps Réel & Serveur HTTP Shelf (
genkit_shelf) : Diffuser la narration d'audit en direct viacontext.sendChunk()tout en retournant un objet final typéOrderAuditResultexposé surshelf_router. - Client Flutter Streaming (
apps/flutter_client) : Consommer les flows distants en mode unitaire et streaming avecdefineRemoteActiondepackage:genkit/client.dart.
| Architectural Layer | Client-Direct LLM Anti-Pattern | Full-Stack Genkit Dart + Shelf |
|---|---|---|
| API Key Custody | Exposed inside APK/IPA binary | 100% isolated in server environment |
| Contract Type Safety | Unverified raw JSON maps | Shared Schemantic classes (Server + Flutter) |
| Tool Execution Trust | Untrusted client-side callbacks | Authoritative server tools + HITL middleware |
| Observability & Tracing | Blind console logs on user devices | Full step-by-step spans in Genkit Dev UI |
3. Prerequisites & System Requirements
Ensure your workstation meets the following requirements before starting Step 2:
- Dart SDK:
>=3.12.0(or Flutter SDK>=3.29.0bundling modern Dart). - Genkit CLI: Installed globally to launch the local reflection & trace inspector UI on port
4000. - Google AI Gemini API Key: Exported as the
GEMINI_API_KEYenvironment variable on your server machine (never hardcoded in source files).
Run the pre-flight verification commands below in your terminal:
3. Prérequis & Configuration Système
Vérifiez que votre poste de travail respecte les prérequis suivants avant de passer à l'étape 2 :
- SDK Dart :
>=3.12.0(ou SDK Flutter>=3.29.0incluant Dart moderne). - Genkit CLI : Installé globalement pour lancer l'interface locale d'inspection des traces sur le port
4000. - Clé API Google AI Gemini : Définie dans la variable d'environnement
GEMINI_API_KEYcôté serveur (jamais écrite en dur dans le code source).
Exécutez les commandes de vérification pré-vol ci-dessous dans votre terminal :
# 1. Verify Dart and Flutter SDK versions
dart --version
flutter --version
# 2. Install the Genkit CLI globally (choose cURL or npm)
curl -sL cli.genkit.dev | bash
# OR via npm:
npm install -g genkit-cli
# 3. Verify Genkit CLI installation
genkit --version