Logoflutter_gemma

Installation

Add the packages, register engines, and complete per-platform setup for iOS, Android, Web, and Desktop.

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)

  # Optionalon-device speech (STT + TTS):
  flutter_gemma_speech: latest_version       # transcribe audio + synthesize speech (on-device STT + TTS; native only) + voice loop

  # Optionalon-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.

**Migrating from 0.16.x (monolith)?** See the [Migration guide](/docs/migration) — the only breaking change is adding the opt-in packages and the `initialize(...)` call; every model / session / RAG API is unchanged.

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:

ParameterProvided byNotes
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).
Use `WebStorageMode.streaming` when shipping `.litertlm` web models — the `@litert-lm/core` engine consumes an OPFS ReadableStream and avoids Chrome's ~2 GB blob-fetch limit on Gemma 4 E2B/E4B web builds.

3. Platform-specific setup#

Complete platform-specific setup before using the plugin.

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>
No host-side `Podfile` `post_install` is required on iOS — flutter_gemma patches the upstream LiteRT-LM `dlopen` path to use `@executable_path/Frameworks/.framework/` so dyld resolves Metal accelerators directly through the Native-Assets-bundled framework. This also keeps `Runner.app/Frameworks/` App-Store-clean (fixes ITMS-90432).

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.**
`flutter_gemma_litertlm` is delivered as a Native-Assets dylib (no MediaPipe Java classes), so it needs no ProGuard rules.

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 featurearm64-v8ax86_64armeabi-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' }
    }
}
Anything backed by `libLiteRtLm.so` on Android — `.litertlm` inference, embeddings, and speech (STT + TTS) — requires **minSdk 30**: the library depends on API 30+ Bionic syscalls (`pthread_cond_clockwait`, `sem_clockwait`) that cannot be shimmed on older devices. MediaPipe `.task` models work on lower API levels.

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
**Model compatibility:** mobile `.task` models often don't work on web — use the `-web.task` (MediaPipe) or `.litertlm` (LiteRT-LM) web variant. Check the model repo for web-compatible builds.

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.

PlatformSupported architectureNot supported
Androidarm64-v8a (full)armeabi-v7a, x86_64 ¹
iOS devicearm64
iOS Simulatorarm64 (Apple Silicon Mac)x86_64 (Intel Mac)
macOSarm64 (Apple Silicon)x86_64 (Intel Mac)
Linuxx86_64, arm64
Windowsx86_64arm64

¹ 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.

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:

LevelWhat it prints (debug only)
GemmaLogLevel.noneNothing — fully silent.
GemmaLogLevel.info (default)Lifecycle, errors, diagnostics. No model output / prompts.
GemmaLogLevel.verboseEverything 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.