LogoFlutter Edge AI

Models

Supported models, file formats, capabilities, ModelType reference, and download URLs.

flutter_edge_ai supports Gemma 4, Gemma3n, FastVLM, Qwen2-VL, SmolVLM2, LLaVA-OneVision, Gemma 3, FunctionGemma, Qwen3, Qwen 2.5, Phi-4 (incl. Phi-4 Mini Reasoning), DeepSeek R1, SmolLM, SmolLM3 and more. On desktop (macOS, Windows, Linux) LiteRT-LM models must be .litertlm — there is no MediaPipe engine there; ONNX models and the OS built-in model also run on desktop.

For a small custom model, LiteTune provides the easier end-to-end path from Hugging Face data/checkpoints through fine-tuning, .litertlm conversion, verification, and bundling. It is alpha software, so verify the converted model on the devices and backends you plan to ship.

Model file types#

Flutter Edge AI supports different model file formats, grouped into three types based on how chat templates are handled.

Type 1: SDK-managed templates#

  • .task files: MediaPipe-optimized format for mobile (Android/iOS) and web.
  • .litertlm files: LiteRT-LM format for Android, iOS, Desktop, and web (early preview). Never handled by MediaPipe — the two formats go to different engines.

Chat templates are applied by the runtime rather than by your code — MediaPipe for .task, LiteRT-LM for .litertlm — on every platform, iOS included.

Type 2: Manual template formatting#

  • .bin files: standard binary format.
  • .tflite files: LiteRT format (formerly TensorFlow Lite).

Both formats require manual chat template formatting in your code.

Type 3: System OS models (no file)#

Gemini Nano (Android, via AICore / ML Kit GenAI), Apple Foundation Models (iOS 26+/macOS), Windows AI Foundry / Phi Silica (Windows), and Gemini Nano via the browser Prompt API (Web — Gemini Nano in Chrome, Phi-4-mini in Edge) are built into the OS/browser — there is no model file to bundle or download; the platform owns the weights. Add flutter_edge_ai_builtin_ai, register BuiltInAiEngine(), and use ModelFileType.builtIn. Availability is device-gated — Gemini Nano needs Pixel 9+/Galaxy S25+, Apple FM needs Apple Intelligence enabled on iPhone 15 Pro+/M-series, and Windows needs Windows 11 25H2+ on Copilot+-class hardware and a packaged app (nothing to configure to build — flutter_local_ai resolves the Windows App SDK projection itself). On Android the package requires minSdk 26 and Kotlin 2.3.21.

👉 See Built-in AI for the full setup, the availability probe (BuiltInAi.availability() / BuiltInAi.ensureReady()), and fallback guidance.

ModelFileType is what selects the engine — it is not inferred from the file name. installModel defaults it to ModelFileType.task, so declare it explicitly: ModelFileType.litertlm for .litertlm files (omitting it routes the model to MediaPipe, which cannot read that format), ModelFileType.task for .task, ModelFileType.binary for .bin and .tflite, ModelFileType.onnx for ONNX models (flutter_edge_ai_onnx), and ModelFileType.builtIn for OS-provided models.

Format by platform#

Format Android iOS Web Desktop Use Case
.task ✅ ✅ ✅ ❌ Older models (Gemma3n, Gemma 3, DeepSeek, Qwen 2.5, Phi-4)
.litertlm ✅ ✅ ¹ ⚠️ ² ✅ Newer models (Gemma 4, Qwen3, FastVLM + desktop for all)
-web.task ❌ ❌ ✅ ❌ Web-specific MediaPipe builds (Gemma 4 in the current catalog)
.bin ✅ ✅ ✅ ❌ Manual chat template formatting required
.tflite ✅ ✅ ✅ ✅ Embeddings only (EmbeddingGemma, Gecko)
onnx ✅ ✅ ✅ ✅ ONNX models — an ORT-GenAI model directory on native, a Hugging Face repo via Transformers.js on Web — ONNX
builtIn (no file) ✅ ✅ ✅ ⚠️ ³ OS/browser model — Built-in AI

¹ iOS .litertlm runs on the FFI engine — vision and audio supported on physical devices. The Simulator stays CPU-only because Metal sim has a 256 MB single-allocation cap.

² Web .litertlm is an early preview via @litert-lm/core — text plus function calling. No vision, audio, the Gemma 4 thinking channel, or LoRA; Qwen3 reasoning tags are still parsed when the model emits them. See the feature matrix in Troubleshooting. For full multimodal on web, use a MediaPipe .task build.

³ On desktop the OS model exists on macOS (Apple Foundation Models) and Windows (AI Foundry; Windows 11 25H2+ on Copilot+-class hardware, packaged app) — not on Linux. Every platform still gates it at runtime: probe with BuiltInAi.availability().

Model capabilities#

Model Family Best For Function Calling Thinking Mode Vision Languages Size
Gemma 4 E2B Next-gen multimodal chat — text, image, audio ✅ ✅ ¹ ✅ Multilingual 2.4GB
Gemma 4 E4B Next-gen multimodal chat — text, image, audio ✅ ✅ ¹ ✅ Multilingual 4.3GB
Gemma3n On-device multimodal chat and image analysis ⚠️ ² ❌ ✅ Multilingual 3-6GB
FastVLM 0.5B Fast vision-language inference ❌ ❌ ✅ Multilingual 0.5GB
Qwen2-VL 2B Vision-language chat (image + text) ❌ ❌ ✅ Multilingual 1.8GB
SmolVLM2 500M Compact vision-language model ❌ ❌ ✅ Multilingual 0.36GB
LLaVA-OneVision 0.5B Compact vision-language model ❌ ❌ ✅ Multilingual 0.83GB
Phi-4 Mini Advanced reasoning and instruction following ✅ ❌ ❌ Multilingual 3.9GB
Phi-4 Mini Reasoning Step-by-step reasoning ❌ ⚠️ ‡ ❌ Multilingual 2.8GB
DeepSeek R1 High-performance reasoning and code generation ✅ ✅ ❌ Multilingual 1.7GB
Qwen3 0.6B Compact multilingual chat with function calling ✅ ✅ ❌ Multilingual 586MB
Qwen 2.5 Strong multilingual chat and instruction following ✅ ❌ ❌ Multilingual 0.5-1.6GB
Gemma 3 1B Balanced and efficient text generation ❌ ❌ ❌ Multilingual 0.5GB
Gemma 3 270M Ideal for fine-tuning (LoRA) for specific tasks ❌ ❌ ❌ Multilingual 0.3GB
FunctionGemma 270M Specialized for function calling on-device ✅ ❌ ❌ Multilingual 284MB
SmolLM 135M Ultra-compact, resource-constrained devices ❌ ❌ ❌ English 135MB
LFM2.5 230M Smallest entry; no HF token needed ❌ ❌ ❌ Multilingual 168MB
SmolLM3 3B Multilingual small LLM with reasoning mode ❌ ⚠️ ‡ ❌ Multilingual 2.0GB
TranslateGemma 4B † Single-shot 55-language translation ❌ ❌ ❌ 55 languages 2-4GB

¹ Gemma 4 Thinking Mode needs the native extraContext channel: Android, iOS and Desktop only. The measured Web .litertlm test receives no Gemma 4 ThinkingResponse; MediaPipe Web cannot enable it and ONNX Web ignores it. Qwen3 is different: core parses its emitted <think> tags on every platform, including Web.

² Gemma3n function calling: on the downloadable E4B .litertlm build; not on E2B or the MediaPipe .task builds.

‡ Reasons, but emits no ThinkingResponse. These models run as ModelType.general, which has no reasoning parser — their thinking blocks arrive inside the answer as ordinary text and are not stripped. See Thinking Mode.

† TranslateGemma is CPU-only for now. Google hasn't released a mobile/desktop .litertlm bundle (HF discussion #5). The community-converted bundle from barakplasma/translategemma-4b-it-android-task-quantized runs correctly on the CPU and returns only padding on the GPU.

Measured here on an M4 Pro (macOS, Metal), both published artifacts, same prompt and the same session code, backend the only variable:

artifactPreferredBackend.cpuPreferredBackend.gpu
int4-generic (2.0 GB, INT4 blockwise)Guten Morgen997 <pad> tokens, nothing else
dynamic_int8-generic (3.9 GB, INT8 channelwise)Guten Morgen997 <pad> tokens, nothing else

Nothing fails: the Metal engine is created, activeBackend reports gpu (so it is not a silent fallback), generation runs and emits padding until it reaches the context limit. A gemma-4-E2B-it.litertlm bundle on the same machine, the same code path and the same backend answers correctly with no padding, so this is not the Metal path in general. Earlier revisions of this page said the bundle crashed the LiteRT GPU partitioner because its EMBEDDING_LOOKUP weights stay float32. Nothing in the runs above crashes, so that description does not hold; the repository has published these .litertlm artifacts since 2026-03-31, so it did not describe a later change either. Reproducing across two different quantization recipes also rules the bit width out.

Tracked at LiteRT-LM#1748. Use PreferredBackend.cpu for this model (≈90 s prefill on a 4 B int4 bundle on M-series Macs). If you are converting it yourself, AI Edge Quantizer is the supported route today — the litert-lm quantization CLI announced in that thread never shipped — with published recipes and Model Explorer for the layer regexes (maintainer's pointers).

ModelType reference#

When installing models, specify the correct ModelType:

Model FamilyModelTypeExamples
Gemma 4 ModelType.gemma4 Gemma 4 E2B, Gemma 4 E4B (native function-call tokens)
Gemma 3 / Gemma3n ModelType.gemmaIt Gemma 3 1B, Gemma 3 270M, Gemma3n E2B/E4B
DeepSeekModelType.deepSeekDeepSeek R1
Qwen 2.5 ModelType.qwen Qwen 2.5 1.5B, Qwen 2.5 0.5B
Qwen 3ModelType.qwen3Qwen3 0.6B
Phi-4 ModelType.phi Phi-4 Mini (parses Phi's own tool-call markers)
FunctionGemma ModelType.functionGemma FunctionGemma 270M IT
General ModelType.general FastVLM 0.5B, SmolLM 135M, LFM2.5 230M, SmolLM3 3B, Phi-4 Mini Reasoning, Qwen2-VL 2B, SmolVLM2 500M, LLaVA-OneVision 0.5B

Gemma 4 (ModelType.gemma4) and FunctionGemma on a .litertlm route their native tool-call tokens through the LiteRT-LM SDK's chat-template path. For Gemma 3 and earlier, keep ModelType.gemmaIt; a .task FunctionGemma keeps the text format flutter_edge_ai renders itself.

Usage example:

// Gemma models
await FlutterEdgeAi.installModel(modelType: ModelType.gemmaIt)
  .fromNetwork(url).install();

// DeepSeek models
await FlutterEdgeAi.installModel(modelType: ModelType.deepSeek)
  .fromNetwork(url).install();

// Phi-4 (its own type — parses Phi's tool-call markers)
await FlutterEdgeAi.installModel(modelType: ModelType.phi)
  .fromNetwork(url).install();

Supported models & platforms#

Model Size Desktop Mobile Web
Gemma 4 E2B 2.4GB ✅ ✅ ✅
Gemma 4 E4B 4.3GB ✅ ✅ ✅
Gemma3n E2B 3.1GB ✅ ✅ ✅
Gemma3n E4B 6.5GB ✅ ✅ ✅
FastVLM 0.5B 0.5GB ✅ ❌ ❌
Qwen2-VL 2B 1.8GB ✅ ✅ ❌
SmolVLM2 500M 0.36GB ✅ ✅ ❌
LLaVA-OneVision 0.5B 0.83GB ✅ ✅ ❌
Gemma-3 1B 0.5GB ✅ ✅ ✅
Gemma 3 270M 0.3GB ✅ ✅ ✅
FunctionGemma 270M 284MB ✅ ✅ ✅
Qwen3 0.6B 586MB ✅ ✅ ✅
Qwen 2.5 1.5B 1.6GB ✅ ✅ ❌
Qwen 2.5 0.5B 0.5GB ❌ ✅ ❌
SmolLM 135M 135MB ❌ ✅ ❌
LFM2.5 230M 168MB ✅ ✅ ❌
SmolLM3 3B 2.0GB ✅ ✅ ❌
Phi-4 Mini 3.9GB ✅ ✅ ✅
Phi-4 Mini Reasoning 2.8GB ✅ ✅ ❌
DeepSeek R1 1.7GB ❌ ✅ ❌

Installation sources#

// Network — .litertlm is the cross-platform default (Android/iOS/Desktop).
// For mobile-only or web-only apps you can substitute a .task URL — and drop
// the fileType, which defaults to ModelFileType.task.
await FlutterEdgeAi.installModel(
  modelType: ModelType.gemmaIt,
  fileType: ModelFileType.litertlm,
)
  .fromNetwork('https://example.com/model.litertlm', token: 'optional')
  .install();

// Flutter assets
await FlutterEdgeAi.installModel(
  modelType: ModelType.gemmaIt,
  fileType: ModelFileType.litertlm,
)
  .fromAsset('assets/models/model.litertlm')
  .install();

// Native bundle
await FlutterEdgeAi.installModel(
  modelType: ModelType.gemmaIt,
  fileType: ModelFileType.litertlm,
)
  .fromBundled('model.litertlm')
  .install();

// External file (native only)
await FlutterEdgeAi.installModel(
  modelType: ModelType.gemmaIt,
  fileType: ModelFileType.litertlm,
)
  .fromFile('/path/to/model.litertlm')
  .install();

Hugging Face#

Install a model straight from a Hugging Face repo. The resolver that reads a repo's deployment manifest ships with the engine, so registering the engine is enough — there is no separate resolver list to maintain.

One call (manifest-driven) — omit file, and fromHuggingFace(repo) resolves the repo's manifest (e.g. litertlm_manifest.json) at install time, installs the right revision-pinned variant for the device, and returns the manifest's overridable runtime defaults on the result:

// The engine carries its resolver — nothing else to register.
await FlutterEdgeAi.initialize(inferenceEngines: [LiteRtLmEngine()]);

final install = await FlutterEdgeAi.installModel(
  modelType: ModelType.general,     // fallback — the manifest overrides it
  fileType: ModelFileType.litertlm, // selects the litertlm resolver
)
  .fromHuggingFace('litert-community/Qwen3-4B-Thinking-2507')
  .install();

final model = await FlutterEdgeAi.getActiveModel(defaults: install.runtime);
// `minOutputTokens` is a FLOOR, not a cap — leave `maxOutputTokens` unset (or
// keep it >= `install.runtime?.minOutputTokens`). Passing the floor as the cap
// would truncate a reasoning model mid-thought.
final session = await model.createSession(
  enableThinking: install.runtime?.isThinking ?? false,
);

.onnx repos install the whole ORT-GenAI directory — the ONNX resolver (carried by OnnxEngine) lists the repo, picks a CPU execution-provider folder (the bundled runtime is CPU-only; pin one with OnnxHuggingFaceResolver(variant: …)) and downloads every file in it; see ONNX. .builtIn repos surface that OS models have no Hugging Face file.

Explicit file — pass file, and fromHuggingFace(repo, file:) resolves …/resolve/<revision>/<file> and installs it directly, for any fileType (no manifest needed; the HF token is applied to huggingface.co automatically):

await FlutterEdgeAi.installModel(
  modelType: ModelType.gemmaIt,
  fileType: ModelFileType.litertlm,
)
  .fromHuggingFace('litert-community/Gemma3-1B-IT', file: 'model.litertlm', token: 'optional')
  .install();

Inspect first — resolveHuggingFace(repo, fileType:) returns the resolved identity plus overridable runtime defaults WITHOUT installing, so you can inspect the variant, its notes, and defaults before committing:

final r = await FlutterEdgeAi.resolveHuggingFace(
  'litert-community/Qwen3-4B-Thinking-2507',
  fileType: ModelFileType.litertlm,
);
// … inspect r.file / r.notes / r.runtime …
await FlutterEdgeAi.installModel(
  modelType: r.modelType ?? ModelType.general,
  fileType: r.fileType,
)
  .fromNetwork(r.url) // r.url pins the resolver's revision
  .install();

The resolver lives in the engine package — LitertlmManifestResolver in flutter_edge_ai_litertlm reads litertlm_manifest.json — and rides on the engine via HuggingFaceResolverSource, so initialize(inferenceEngines: […]) auto-registers it. Pass initialize(huggingFaceResolvers: [...]) only to override an engine's default (e.g. LitertlmManifestResolver(revision: 'abc123') to pin a commit).

Source capabilities#

Source Type Platform Progress Resume Authentication Use Case
NetworkSource All ✅ Detailed ⚠️ Server-dependent ✅ Supported HuggingFace, CDNs, private servers
AssetSource All ⚠️ End only ❌ No ❌ N/A Models bundled in app assets
BundledSource All ⚠️ End only ❌ No ❌ N/A Native platform resources
FileSource Native (no Web) ⚠️ End only ❌ No ❌ N/A User-selected files (file picker)

Resume after interruption is server-dependent and not supported by the HuggingFace CDN — flutter_edge_ai uses smart retry logic with exponential backoff and automatic restart instead. See Troubleshooting.

Android foreground service (large downloads)#

Android has a 9-minute background execution limit. A foreground service shows a notification and exempts the download from battery-optimization kills, and you have to ask for it — the default configures no notification, and the platform will not start the service without one, so file size alone changes nothing:

// DEFAULT — no foreground service; pass foreground: true to get one
await FlutterEdgeAi.installModel(modelType: ModelType.gemmaIt)
  .fromNetwork(url)  // foreground: null
  .install();

// Force foreground mode
await FlutterEdgeAi.installModel(modelType: ModelType.gemmaIt)
  .fromNetwork(url, foreground: true)
  .install();

iOS uses native URLSession which handles long downloads automatically — no foreground service needed.

Cancelling downloads#

import 'package:flutter_edge_ai/flutter_edge_ai.dart'; // exports CancelToken

final cancelToken = CancelToken();

final future = FlutterEdgeAi.installModel(modelType: ModelType.gemmaIt)
  .fromNetwork(url)
  .withCancelToken(cancelToken)
  .withProgress((progress) => print('Progress: $progress%'))
  .install();

// Cancel from elsewhere (e.g. user pressed a cancel button)
cancelToken.cancel('User cancelled download');

try {
  await future;
} catch (e) {
  if (CancelToken.isCancel(e)) {
    print('Download was cancelled by user');
  }
}

CancelToken cancels all files in multi-file downloads (e.g. embedding model + tokenizer), works on mobile + web, and throws DownloadCancelledException.

Speech models#

On-device speech via the opt-in flutter_edge_ai_speech package — selectable, profile-driven pipelines (you pick the model with SttModelType / TtsModelType). moonshine-tiny, Whisper, and Parakeet (STT) and Matcha, Qwen3-TTS (multilingual), and Inflect-Nano-v2 (fast) TTS work end-to-end today; kokoro / supertonic TTS voices are follow-ons. Native only (no Web). See Speech for usage. A VoiceSession also chains STT → LLM → TTS into a push-to-talk voice loop — see Speech.

Speech-to-text

ModelInputSizeStatusAuth
moonshine-tiny raw 16 kHz PCM ~104 MB ✅ end-to-end ❌
Whisper (tiny / base) log-mel — ✅ end-to-end ❌
Parakeet (CTC 0.6B) log-mel — ✅ end-to-end ❌

Whisper is multilingual — the shipped checkpoints are the multilingual ones (no .en suffix), so all 99 of Whisper's languages are available. Set a default for the recognizer, override it per transcription, or both:

// Default for every transcription on this recognizer.
final stt = await FlutterEdgeAi.getActiveStt(language: 'de');
final german = await stt.transcribe(germanPcm);

// One call in another language — same recognizer, nothing reloaded.
final french = await stt.transcribe(frenchPcm, language: 'fr');

Both are free: the language is one token in the decoder's seed prompt, and that prompt is rebuilt on every transcription. Switching languages never reloads the model or invalidates the recognizer you are holding.

The value is Whisper's own language code without the delimiters, and it defaults to 'en'. It decides the OUTPUT language only — the weights understand the audio either way, so asking for 'en' on German speech returns an English translation rather than an error.

Moonshine and Parakeet have no language token in their decoder prompt and reject the parameter with an ArgumentError rather than ignoring it; both are English-only.

Text-to-speech

ModelOutputSizeStatusAuth
Matcha 16-bit PCM @ 22050 Hz ~90 MB ✅ end-to-end ❌
Qwen3-TTS (10 langs + auto ) 16-bit PCM ~1.9 GB ✅ end-to-end ❌
Inflect-Nano-v2 (fast) 16-bit PCM @ 24 kHz ~36 MB ✅ end-to-end ❌

Text embedding models#

The LiteRT embedding models below generate 768-dimensional vectors. The numbers in names (64/256/512/1024/2048) indicate maximum input sequence length in tokens, not embedding dimension. (With the ONNX backend the dimension is model-dependent — e.g. all-MiniLM-L6-v2 is 384-dim.) See Embeddings & RAG for usage.

Model Parameters Dimensions Max Seq Length Size Auth Required
Gecko 64 110M 768D 64 tokens 110MB ❌
Gecko 256 110M 768D 256 tokens 114MB ❌
Gecko 512 110M 768D 512 tokens 116MB ❌
EmbeddingGemma 256 300M 768D 256 tokens 179MB ✅
EmbeddingGemma 512 300M 768D 512 tokens 179MB ✅
EmbeddingGemma 1024 300M 768D 1024 tokens 183MB ✅
EmbeddingGemma 2048 300M 768D 2048 tokens 196MB ✅

Performance (Android Pixel 8):

  • Gecko 64: ~109 ms/doc embedding, 130 ms search (fastest — 2.6× faster than EmbeddingGemma).
  • EmbeddingGemma 256: ~286 ms/doc embedding, 342 ms search (more accurate — 300M vs 110M params).

SigLIP 2 text tower (ONNX, manual wiring)#

flutter_edge_ai_embeddings ships a SigLIP 2 text profile, for putting text into the same space as SigLIP's vision tower — image↔text retrieval rather than document RAG. It is the only embedding profile here that is not installed through installEmbedder().

Text in, vectors out — the plugin does not run the vision tower. You embed the image side elsewhere (or offline) and query it with vectors this profile produces.

Its convention differs from every model above: no leading BOS, a single trailing <eos>, lowercased text, and a fixed 64-token width that lives in the token ids because the int8 export carries no attention_mask. It also ignores the TaskType prefix — the vision tower encodes an image with no prefix, so adding one moves the text vector off the space the two towers share.

SigLIP 2 reuses the Gemma BPE vocabulary, so a tokenizer.json cannot be told apart by its vocabulary alone, and the ONNX tokenizer loader refuses such a file rather than reading it with Gemma's convention and returning a plausible but wrong vector.

To tell whether an export is the one this profile expects, look at two blocks of its tokenizer.json — the same two the refusal keys on:

  • "padding" declares a fixed width — "strategy": {"Fixed": …}, not "BatchLongest"
  • "post_processor" appends <eos> and prepends no <bos>

The refusal needs both, so there are three outcomes rather than two. A file matching both is SigLIP 2's convention. One that prepends <bos> is an EmbeddingGemma-family file and belongs on the profiles above. A file failing either check — no padding block, "BatchLongest", or a post_processor that is missing, empty, or does not end in a special token — is not classified at all: it is read with Gemma's convention and nothing is raised.

So do not read the absence of an error as approval. A SigLIP 2 export that declares both blocks is caught, but one that dropped either — a re-export, or a tool that strips them — reaches the Gemma path and produces exactly the silently wrong vector this section warns about.

Wire it yourself:

import 'package:flutter_edge_ai_embeddings/embedding_tokenizer.dart'
    show loadSiglipSentencePieceEmbeddingTokenizer;

and pass that as the tokenizer factory of the ForwardPassDescriptor you give to CommonEmbeddingModel.create. That library is native-only. See the flutter_edge_ai_embeddings README for the full profile, and ONNX Runtime for why the factory declines to guess.