As of 1.0, flutter_gemma is split into a small core package plus
opt-in packages for each engine / backend, so your app only pulls the native
weight it actually uses. Add the core package, then the packages for the model
formats and features you need.
1. Add packages to pubspec.yaml#
dependencies:
flutter_gemma: latest_version # Core — always required (no engine on its own)
# Inference engines — add at least one:
flutter_gemma_litertlm: latest_version # .litertlm models (FFI; mobile + desktop + web) + LiteRtEmbeddingBackend
flutter_gemma_mediapipe: latest_version # .task / .bin models (MediaPipe; mobile + web)
flutter_gemma_builtin_ai: latest_version # OS system models — Gemini Nano (Android) / Apple FM (iOS 26+/macOS) / Chrome Prompt API (Web)
flutter_gemma_onnx: latest_version # ONNX models — ORT-GenAI (FFI, native) / Transformers.js (web) + OnnxEmbeddingBackend
# Optional — text embeddings + on-device RAG:
flutter_gemma_embeddings: latest_version # text-embedding pipeline (needs a backend, e.g. LiteRtEmbeddingBackend above)
flutter_gemma_rag_qdrant: latest_version # RAG vector store (qdrant-edge; fastest on native)
flutter_gemma_rag_sqlite: latest_version # RAG vector store (sqlite-vec / vec0; all platforms, incl. web)
# Optional — on-device speech (STT + TTS):
flutter_gemma_speech: latest_version # transcribe audio + synthesize speech (on-device STT + TTS; native only) + voice loop
# Optional — on-device agent skills:
flutter_gemma_agent: latest_version # agent skills the model runs itself (text / JS / native-intent / MCP)
Pick by need:
| You want to… | Add |
|---|---|
Run .litertlm models (Gemma 4, Qwen3, FastVLM, + all desktop) |
flutter_gemma_litertlm |
Run .task / .bin models (Gemma3n, Gemma 3, DeepSeek, Qwen 2.5, Phi-4) |
flutter_gemma_mediapipe |
| Run the OS system model with no download (Gemini Nano / Apple Foundation Models) | flutter_gemma_builtin_ai |
| Run ONNX models — ORT-GenAI (native) or Transformers.js (Web) | flutter_gemma_onnx |
| Generate text embeddings |
flutter_gemma_embeddings
+
flutter_gemma_litertlm
(
LiteRtEmbeddingBackend
)
|
| Generate text embeddings from ONNX/ORT models |
flutter_gemma_embeddings
+
flutter_gemma_onnx
(
OnnxEmbeddingBackend
)
|
| On-device RAG on native, fastest (Android/iOS/desktop) | flutter_gemma_rag_qdrant |
| On-device RAG on web, or a portable/exact store on any platform | flutter_gemma_rag_sqlite |
| Transcribe audio, synthesize speech, or run a voice loop on-device (STT + TTS + voice) | flutter_gemma_speech |
| Run on-device agent skills the model executes itself (text / JS / native-intent / MCP) | flutter_gemma_agent |
Core registers no engine by itself — you wire the packages you added in
await FlutterGemma.initialize(...) (below). Run flutter pub get
to install.
2. Initialize Flutter Gemma#
Call await FlutterGemma.initialize(...) once in main() and register the opt-in
packages you added to pubspec.yaml. Core registers no engine on its own, so
without this step getActiveModel() / createEmbeddingModel()
throw a clear
"add the engine package" error.
import 'package:flutter/widgets.dart';
import 'package:flutter_gemma/flutter_gemma.dart';
import 'package:flutter_gemma_litertlm/flutter_gemma_litertlm.dart';
import 'package:flutter_gemma_mediapipe/flutter_gemma_mediapipe.dart';
import 'package:flutter_gemma_builtin_ai/flutter_gemma_builtin_ai.dart';
import 'package:flutter_gemma_speech/flutter_gemma_speech.dart';
import 'package:flutter_gemma_rag_qdrant/flutter_gemma_rag_qdrant.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterGemma.initialize(
// Inference engines — add the ones whose packages you depend on:
inferenceEngines: const [
LiteRtLmEngine(), // flutter_gemma_litertlm — .litertlm models
MediaPipeEngine(), // flutter_gemma_mediapipe — .task / .bin models
BuiltInAiEngine(), // flutter_gemma_builtin_ai — Gemini Nano / Apple FM
],
// Optional — embeddings (needed for RAG / generateEmbedding):
embeddingBackends: const [
LiteRtEmbeddingBackend(), // flutter_gemma_litertlm (needs flutter_gemma_embeddings too)
],
// Optional — on-device speech-to-text:
sttBackends: const [
LiteRtSttBackend(), // flutter_gemma_speech
],
// Optional — on-device text-to-speech:
ttsBackends: const [
LiteRtTtsBackend(), // flutter_gemma_speech
],
// Optional — RAG vector store (pick one; native here):
vectorStore: QdrantVectorStore(), // flutter_gemma_rag_qdrant
// Common settings:
// String.fromEnvironment yields '' when the define is absent, and an
// empty token still sends a bare `Authorization: Bearer` header. Pass
// null instead so the request goes out unauthenticated.
huggingFaceToken: const String.fromEnvironment('HUGGINGFACE_TOKEN').isNotEmpty
? const String.fromEnvironment('HUGGINGFACE_TOKEN')
: null,
maxDownloadRetries: 10,
);
runApp(MyApp());
}
Which parameter ← which package:
| Parameter | Provided by | Notes |
|---|---|---|
inferenceEngines: [LiteRtLmEngine()] |
flutter_gemma_litertlm |
.litertlm (mobile + desktop + web) |
inferenceEngines: [MediaPipeEngine()] |
flutter_gemma_mediapipe |
.task / .bin (mobile + web) |
inferenceEngines: [OnnxEngine()] |
flutter_gemma_onnx |
ONNX models — ORT-GenAI (FFI, macOS/Linux/Windows/Android/iOS arm64) or Transformers.js (Web) |
embeddingBackends: [LiteRtEmbeddingBackend()] |
flutter_gemma_litertlm |
text embeddings (needs flutter_gemma_embeddings too) |
embeddingBackends: [OnnxEmbeddingBackend()] |
flutter_gemma_onnx |
text embeddings from ONNX/ORT models (needs flutter_gemma_embeddings too) |
sttBackends: [LiteRtSttBackend()] |
flutter_gemma_speech |
speech-to-text (native only) |
ttsBackends: [LiteRtTtsBackend()] |
flutter_gemma_speech |
text-to-speech (native only) |
vectorStore: QdrantVectorStore() |
flutter_gemma_rag_qdrant |
native RAG |
vectorStore: SqliteVectorStore() / WebSqliteVectorStore() |
flutter_gemma_rag_sqlite |
sqlite-vec RAG (all platforms; WebSqliteVectorStore() on web) |
Add only the engines you ship. Passing both LiteRtLmEngine() and
MediaPipeEngine() lets one app run both formats — the registry routes each
model to the engine that handles its file type. The sqlite-vec store runs on
every platform — use vectorStore: SqliteVectorStore() on native and
WebSqliteVectorStore() on web. flutter_gemma_rag_qdrant is native-only (and
the fastest option there).
Common settings:
huggingFaceToken: authentication token for gated models (Gemma3n, EmbeddingGemma).maxDownloadRetries: number of retry attempts for failed downloads (default: 10).-
webStorageMode(Web only): storage strategy for model files (default:cacheApi).WebStorageMode.cacheApi: Cache API with Blob URLs (for models <2GB).WebStorageMode.streaming: OPFS streaming (for large models >2GB like E4B, 7B).WebStorageMode.none: no caching (ephemeral mode for testing).
3. Platform-specific setup#
iOS#
Required by any engine package: flutter_gemma_litertlm, flutter_gemma_mediapipe
and/or flutter_gemma_builtin_ai.
Set the minimum iOS version to 15.0 — or 16.0 if your app depends on
flutter_gemma_mediapipe, which needs MediaPipe GenAI. Core, flutter_gemma_litertlm,
built-in AI and embeddings build from 15.0. (Requires flutter_gemma 1.6.4 or newer;
earlier versions declared 16.0.)
Where you set it depends on the dependency manager. Swift Package Manager is the
default since Flutter 3.44 (opt-in before that), and an SPM-only app has no Podfile
at all — set iOS
Deployment Target on the Runner target in Xcode, or the build fails with
requires minimum platform version 15.0 … but this target supports 13.0.
flutter_gemma_mediapipe ships no Package.swift, so an app using it also gets a
Podfile; set the platform there as well:
platform :ios, '16.0' # 15.0 if the app does not use flutter_gemma_mediapipe
Declare platform only once — CocoaPods rejects a second one with
Invalid Podfile file: The target 'Pods' already has a platform set.
Change the linking type of pods to static in Podfile:
use_frameworks! :linkage => :static
Enable file sharing in Info.plist:
<key>UIFileSharingEnabled</key>
<true/>
Add a network access description in Info.plist (for development):
<key>NSLocalNetworkUsageDescription</key>
<string>This app requires local network access for model inference services.</string>
Enable performance optimization in Info.plist (optional):
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
Add memory entitlements in Runner.entitlements (for large models):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.developer.kernel.extended-virtual-addressing</key>
<true/>
<key>com.apple.developer.kernel.increased-memory-limit</key>
<true/>
<key>com.apple.developer.kernel.increased-debugging-memory-limit</key>
<true/>
</dict>
</plist>
Android#
Add-to-app hosts must declare the Kotlin Gradle Plugin themselves. Flutter
auto-applies KGP to plugin modules only when the host provides it, so a Java-only native
host fails with Could not find method kotlin(). Add KGP to the host's root
buildscript/plugins {}. A normal flutter build app needs nothing — Flutter's own
Gradle plugin carries KGP.
GPU (any engine): nothing to add. flutter_gemma's own manifest declares the
OpenCL namespace entries and the manifest merger folds them into your app. These
are what your merged manifest must contain if you pin or audit it — note
libvndksupport.so: without it the OpenCL ICD load is denied on Android 12+, the
engine falls back to WebGPU, and some Mali drivers hard-freeze (#324).
<uses-native-library android:name="libvndksupport.so" android:required="false"/>
<uses-native-library android:name="libOpenCL.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-car.so" android:required="false"/>
<uses-native-library android:name="libOpenCL-pixel.so" android:required="false"/>
ProGuard/R8 (only if you use flutter_gemma_mediapipe): the package ships
its own consumer ProGuard rules; from 1.0.6 a release build needs no rules in
your app (built on AGP 9.1). On 1.0.5 and earlier R8 fails the release build
with Missing class (seen on AGP 9) — upgrade to 1.0.6, or add to your
proguard-rules.pro:
-dontwarn com.google.auto.value.**
-dontwarn com.google.mediapipe.proto.CalculatorProfileProto$CalculatorProfile
-dontwarn com.google.mediapipe.proto.GraphTemplateProto$CalculatorGraphTemplate
If a release build then fails at run time with UnsatisfiedLinkError or a
missing MediaPipe class, also add:
# MediaPipe
-keep class com.google.mediapipe.** { *; }
# Protocol Buffers
-keep class com.google.protobuf.** { *; }
-dontwarn com.google.protobuf.**
Android architecture support
MediaPipe text inference (.task / .bin) works on arm64-v8a, x86_64, and
armeabi-v7a. Everything backed by libLiteRtLm (.litertlm inference,
including its vision and audio input, embedding via LiteRT FFI, speech) is
arm64-v8a only:
| Android feature | arm64-v8a | x86_64 | armeabi-v7a |
|---|---|---|---|
Text inference (.task / .bin) | ✅ | ✅ | ✅ |
.litertlm (FFI) | ✅ | ❌ | ❌ |
| Embedding (LiteRT FFI) | ✅ | ❌ | ❌ |
| Speech STT + TTS (LiteRT FFI) | ✅ | ❌ | ❌ |
If your app uses only the arm64-only features, restrict the build to arm64 so the Play Store does not offer broken APKs to incompatible devices:
android {
defaultConfig {
ndk { abiFilters 'arm64-v8a' }
}
}
Web#
On web, MediaPipe ignores preferredBackend and always runs on the GPU
(WebGPU); ONNX honours PreferredBackend.cpu by pinning WASM.
Every web app needs flutter_gemma's model storage helpers. Copy cache_api.js
and opfs_helper.js from the flutter_gemma package's web/ directory into
your app's web/ (find the package directory with
grep -A1 '"name": "flutter_gemma"' .dart_tool/package_config.json), then load
them in web/index.html:
<script src="cache_api.js"></script>
<script src="opfs_helper.js"></script>
Then add the CDN script(s) for the engine package(s) you use.
flutter_gemma_mediapipe (.task / -web.task models):
<script type="module">
import { FilesetResolver, LlmInference } from 'https://cdn.jsdelivr.net/npm/@mediapipe/[email protected]';
window.FilesetResolver = FilesetResolver;
window.LlmInference = LlmInference;
</script>
flutter_gemma_litertlm (.litertlm web models — early preview). The
@litert-lm/core ESM doesn't assign window globals and module scripts are
deferred, so Dart must await window.litertLmReady before any static interop:
<script type="module">
window.litertLmReady = (async () => {
const m = await import('https://cdn.jsdelivr.net/npm/@litert-lm/[email protected]/+esm');
window.Engine = m.Engine;
return m.Engine;
})();
</script>
flutter_gemma_onnx (ONNX models on Web): generation runs on
Transformers.js v4, embeddings
on onnxruntime-web. Both are readiness-handshake shims, same shape as the
litertLmReady promise above:
<script type="module">
window.transformersReady = (async () => {
const m = await import('https://cdn.jsdelivr.net/npm/@huggingface/[email protected]');
window.transformers = m;
return m;
})();
</script>
<script type="module">
window.ortReady = (async () => {
const m = await import('https://cdn.jsdelivr.net/npm/[email protected]/dist/ort.bundle.min.mjs');
m.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/';
window.ort = m;
return m;
})();
</script>
Only add the shim(s) for the arm(s) you use — transformersReady for
OnnxEngine, ortReady for OnnxEmbeddingBackend.
LiteRtEmbeddingBackend (web embeddings, flutter_gemma_litertlm): runs on
LiteRT.js through flutter_gemma_embeddings' web/litert_embeddings.js. Load it
pinned to a release tag with a Subresource-Integrity hash — the
flutter_gemma_embeddings README
has the tag and how to compute the hash:
<script type="module"
src="https://cdn.jsdelivr.net/gh/DenisovAV/flutter_gemma@<tag>/packages/flutter_gemma_embeddings/web/litert_embeddings.js"
integrity="sha384-<hash>"
crossorigin="anonymous"></script>
flutter_gemma_rag_sqlite (web RAG): no <script>. Copy the package's
web/rag/sqlite3.wasm (a sqlite3.wasm with sqlite-vec statically linked)
into your app's web root as rag/sqlite3.wasm, and serve the app with the
cross-origin isolation headers OPFS persistence needs:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Desktop (macOS, Windows, Linux)#
Desktop is served primarily by flutter_gemma_litertlm (.litertlm
files) — the default engine, whose native library is fetched at build time by the
package's Native-Assets hook (no manual download/bundling). flutter_gemma_onnx
(ONNX Runtime) also runs on all three desktop OSes
(macOS/Windows/Linux), and on macOS the OS built-in model is available via
flutter_gemma_builtin_ai (Apple Foundation Models,
macOS only — not Windows/Linux). What holds across all of desktop: there is no
MediaPipe engine on desktop — .task / .bin models are NOT compatible with
desktop.
See Desktop Support for the full per-platform reference (macOS
Podfile post_install, entitlements, Windows VC++ runtime, Linux Vulkan
driver, and known limitations).
Platform & architecture support#
The plugin ships native prebuilts only for the architectures below. Other ABIs fail at native load with a typed error.
| Platform | Supported architecture | Not supported |
|---|---|---|
| Android | arm64-v8a (full) | armeabi-v7a, x86_64 ¹ |
| iOS device | arm64 | — |
| iOS Simulator | arm64 (Apple Silicon Mac) | x86_64 (Intel Mac) |
| macOS | arm64 (Apple Silicon) | x86_64 (Intel Mac) |
| Linux | x86_64, arm64 | — |
| Windows | x86_64 | arm64 |
¹ MediaPipe text inference also works on Android x86_64 / armeabi-v7a (see
the Android section above).
For development, prefer an Apple Silicon Mac — the Android emulator runs
arm64-v8a natively, and macOS / iOS Simulator builds are arm64.
HuggingFace authentication#
Many models require authentication to download from HuggingFace. Never commit tokens to version control.
Recommended: config.json pattern#
Create a config template config.json.example:
{
"HUGGINGFACE_TOKEN": ""
}
Copy it and add your token from huggingface.co/settings/tokens:
cp config.json.example config.json
Add config.json to .gitignore, then run with the config:
flutter run --dart-define-from-file=config.json
Access it in code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
const token = String.fromEnvironment('HUGGINGFACE_TOKEN');
await FlutterGemma.initialize(
huggingFaceToken: token.isNotEmpty ? token : null,
);
runApp(MyApp());
}
Which models require authentication?#
Gated (auth required): Gemma3n (E2B, E4B), Gemma 3 1B, Gemma 3 270M, EmbeddingGemma.
Public (no auth): Gemma 4 (the litert-community builds), DeepSeek, Qwen3, Qwen 2.5, SmolLM, LFM2.5, Phi-4, FastVLM.
To use a gated repo: visit the model page → "Request Access" button.
Logging#
The plugin's internal logs are silent in release builds — model output,
prompts, and conversation history are never written to logcat / syslog. In debug
builds they're shown according to FlutterGemma.logLevel:
| Level | What it prints (debug only) |
|---|---|
GemmaLogLevel.none | Nothing — fully silent. |
GemmaLogLevel.info (default) | Lifecycle, errors, diagnostics. No model output / prompts. |
GemmaLogLevel.verbose | Everything above plus model output, prompts, and conversation history. |
import 'package:flutter_gemma/flutter_gemma.dart';
// See the model's generated tokens and prompts while debugging:
FlutterGemma.logLevel = GemmaLogLevel.verbose;
// Or silence the plugin entirely:
FlutterGemma.logLevel = GemmaLogLevel.none;
Release builds are always silent regardless of this setting. The level is process-global and per-isolate; set it once at startup.