flutter_whisper_ggml
Cross-platform, offline speech recognition for Flutter powered by a bundled and
pinned whisper.cpp v1.9.2 runtime.
The plugin runs inference locally, accepts WAV audio, reports progress and live segments, and exports timestamped results as text, JSON or SRT. Native platforms use Dart FFI; Web runs whisper.cpp in a Web Worker through WebAssembly.
Highlights:
- Android, iOS, Linux, macOS, Web and Windows support;
- automatic download and verified cache for 34 official GGML models;
- manual model paths, application assets, HTTP URLs and browser file selection;
- CPU plus optional CUDA/Vulkan acceleration on supported desktop builds;
- language detection, translation, greedy/beam decoding, prompts, timestamps, VAD controls, cancellation and live segments;
- no FFmpeg or model binaries bundled into the package.
0.0.1is a beta release. The public API may change before1.0.0.
Contents
- Platform support
- Quick start
- Audio input
- Models
- Transcription controls
- Device and GPU selection
- Output
- WebAssembly
- Examples
- Troubleshooting
- Build and validation
- Known limitations
Platform support
| Platform | Runtime | Acceleration | Current validation |
|---|---|---|---|
| Windows x64 | Dart FFI | CPU, CUDA or Vulkan | CI build and manual use |
| Linux x64 | Dart FFI | CPU or Vulkan | CPU/Vulkan CI builds |
| Android 24+ | Dart FFI/NDK | CPU/NEON, ARM64 and x86_64 | Release CI build |
| iOS 13+ | Dart FFI/CocoaPods | CPU + Accelerate | Simulator and unsigned ARM64 CI builds |
| macOS 10.15+ | Dart FFI/CocoaPods | CPU + Accelerate | Release CI build |
| Web | Web Worker + WebAssembly | CPU SIMD/pthreads | CI build and manual use |
iOS and macOS compile in CI but have not yet been validated on physical Apple hardware. Metal, Android Vulkan and WebGPU are not enabled. CUDA and Vulkan only appear in device enumeration when they were compiled into the native library.
Quick start
1. Install
dependencies:
flutter_whisper_ggml: ^0.0.1
import 'package:flutter_whisper_ggml/flutter_whisper.dart';
The package already contains whisper.cpp. Consumers do not need a separate source checkout or system installation.
2. Allow automatic downloads on Android
Only applications that call FlutterWhisper.loadModel or
WhisperModelManager.prepare need network access. Add this outside the
<application> element in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Applications that provide a local model can omit this permission.
3. Download, load and transcribe
final whisper = await FlutterWhisper.loadModel(
model: WhisperModels.smallQ5_1,
config: const WhisperModelConfig(useGpu: false),
onDownloadProgress: (progress) {
print('download: ${progress.percent}%');
},
onModelProgress: (progress) {
print('model: $progress%');
},
);
try {
final result = await whisper.transcribeFile(
'/path/to/speech.wav',
config: const WhisperTranscribeConfig(
language: 'pt',
threads: 4,
),
onProgress: (progress) => print('inference: $progress%'),
onSegment: (segment) => print(segment.text),
);
print(result.text);
print(result.toSrt());
print(result.toJsonString());
} finally {
await whisper.dispose();
}
The first native call downloads and verifies the model. Later calls reuse the cache and inference is fully offline. On Web, reuse depends on normal browser HTTP caching.
Load an existing model
final whisper = await FlutterWhisper.load(
modelPath: '/path/to/ggml-small-q5_1.bin',
config: const WhisperModelConfig(
useGpu: true,
flashAttention: true,
),
onModelProgress: (progress) => print('model: $progress%'),
);
One FlutterWhisper instance owns one model context. Calls on the same instance
are serialized. Always call dispose() when the session is no longer needed.
Audio input
The plugin intentionally accepts WAV only. The most predictable input is:
- mono;
- 16 kHz;
- signed 16-bit PCM;
- speech normalized without clipping.
The Dart decoder also reads supported PCM/Float WAV variants, mixes multiple channels to mono and resamples to 16 kHz. Canonical PCM16 remains recommended because it removes ambiguity before inference.
MP3, AAC, M4A, MP4 and other compressed containers are outside this package.
Convert them in the application before calling transcribeFile. The full
example demonstrates optional FFmpeg conversion without making FFmpeg a plugin
dependency.
Already decoded mono 16 kHz samples can be passed directly:
final result = await whisper.transcribe(
samples,
config: const WhisperTranscribeConfig(language: 'auto'),
);
Models
Models are not included in the package archive. WhisperModels.values contains
all 34 variants supported by the bundled upstream download scripts.
Recommended models
Choose a multilingual model — one without .en — for Brazilian Portuguese.
| Goal | Suggested model | Approximate download |
|---|---|---|
| Smoke test or very constrained device | tiny-q5_1 |
31 MiB |
| Lightweight mobile use | base-q5_1 |
57 MiB |
| Balanced Portuguese/mobile/desktop | small-q5_1 |
181 MiB |
| Strong desktop quality and speed | large-v3-turbo-q5_0 |
547 MiB |
| Maximum available quality | large-v3 |
2.88 GiB |
.en models recognize English only. q5 and q8 variants are quantized: they
use less storage and memory, with a possible accuracy tradeoff. Large models can
exceed browser, mobile or GPU memory limits even when the download succeeds.
Automatic cache
final modelPath = await WhisperModelManager.prepare(
WhisperModels.largeV3TurboQ5_0,
directoryPath: optionalModelsDirectory,
forceDownload: false,
onProgress: (progress) {
print('${progress.receivedBytes}/${progress.totalBytes}');
},
);
On native platforms, the default location is a flutter_whisper_ggml/models
folder inside the application-support directory. Downloads:
- stream directly to disk instead of buffering the model in memory;
- use a temporary
.partfile; - verify exact size and SHA-256;
- retain a
.sha256cache marker after successful verification; - preserve an existing model if a replacement download fails.
Set modelDirectory/directoryPath when the application needs to own the cache
location. Set forceDownload: true to replace a cached copy. Cache deletion is
currently application-managed.
On Web, prepare returns the official HTTPS URL. The Web Worker downloads the
model during loadModel, and onModelProgress reports that operation.
Files named for-tests-* in upstream whisper.cpp contain headers but no model
weights. They cannot transcribe audio and are rejected by the native loader.
All model downloads
Links and exact catalog metadata come from the model repositories used by the whisper.cpp v1.9.2 download scripts.
| Model | Size | Variant |
|---|---|---|
tiny |
74 MiB | Multilingual |
tiny-q5_1 |
31 MiB | Multilingual, quantized |
tiny-q8_0 |
42 MiB | Multilingual, quantized |
tiny.en |
74 MiB | English only |
tiny.en-q5_1 |
31 MiB | English only, quantized |
tiny.en-q8_0 |
42 MiB | English only, quantized |
base |
141 MiB | Multilingual |
base-q5_1 |
57 MiB | Multilingual, quantized |
base-q8_0 |
78 MiB | Multilingual, quantized |
base.en |
141 MiB | English only |
base.en-q5_1 |
57 MiB | English only, quantized |
base.en-q8_0 |
78 MiB | English only, quantized |
small |
465 MiB | Multilingual |
small-q5_1 |
181 MiB | Multilingual, quantized |
small-q8_0 |
252 MiB | Multilingual, quantized |
small.en |
465 MiB | English only |
small.en-q5_1 |
181 MiB | English only, quantized |
small.en-q8_0 |
252 MiB | English only, quantized |
small.en-tdrz |
465 MiB | English, experimental speaker turns |
medium |
1.43 GiB | Multilingual |
medium-q5_0 |
514 MiB | Multilingual, quantized |
medium-q8_0 |
785 MiB | Multilingual, quantized |
medium.en |
1.43 GiB | English only |
medium.en-q5_0 |
514 MiB | English only, quantized |
medium.en-q8_0 |
785 MiB | English only, quantized |
large-v1 |
2.88 GiB | Multilingual |
large-v2 |
2.88 GiB | Multilingual |
large-v2-q5_0 |
1.01 GiB | Multilingual, quantized |
large-v2-q8_0 |
1.54 GiB | Multilingual, quantized |
large-v3 |
2.88 GiB | Multilingual, highest quality |
large-v3-q5_0 |
1.01 GiB | Multilingual, quantized |
large-v3-turbo |
1.51 GiB | Multilingual, faster large model |
large-v3-turbo-q5_0 |
547 MiB | Multilingual, recommended desktop balance |
large-v3-turbo-q8_0 |
834 MiB | Multilingual, higher-precision quantization |
Transcription controls
WhisperTranscribeConfig exposes:
- automatic or explicit language;
- translation to English;
- greedy or beam-search sampling;
- thread count, offset and duration;
- initial prompt and context control;
- token timestamps and segment/token length controls;
- temperature fallback and probability thresholds;
- TinyDiarize and Silero VAD parameters.
Example with beam search and live segments:
final result = await whisper.transcribeFile(
wavPath,
config: const WhisperTranscribeConfig(
language: 'auto',
strategy: WhisperSamplingStrategy.beamSearch,
beamSize: 5,
tokenTimestamps: true,
splitOnWord: true,
),
onProgress: (progress) => updateProgress(progress / 100),
onSegment: appendLiveSegment,
);
Cancellation is cooperative:
whisper.cancel();
The active operation finishes after native inference reaches a cancellation checkpoint. Disposing a session waits for its queued work.
Silero VAD requires a separate compatible VAD model path in WhisperVadConfig;
the speech-model catalog does not download VAD models automatically.
Device and GPU selection
final devices = FlutterWhisper.availableDevices;
for (final device in devices) {
print('${device.name}: ${device.type}');
print('memory: ${device.freeMemory}/${device.totalMemory}');
}
final selected = devices.first;
final whisper = await FlutterWhisper.load(
modelPath: modelPath,
config: WhisperModelConfig.forDevice(selected),
);
CPU is the safest fallback when a model does not fit in GPU memory. Web device enumeration currently exposes only the WebAssembly CPU runtime.
Windows backend
AUTO selects Vulkan when a usable Vulkan SDK is detected at build time and
falls back to CPU otherwise.
$env:FLUTTER_WHISPER_GPU_BACKEND='CPU' # CPU, CUDA, VULKAN or AUTO
flutter build windows --release
CUDA requires the CUDA Toolkit. Vulkan requires a Vulkan SDK and shader compiler during the native build. Backend selection changes what is compiled; it cannot enable a GPU in an already-built binary.
Linux backend
FLUTTER_WHISPER_GPU_BACKEND=CPU flutter build linux --release
FLUTTER_WHISPER_GPU_BACKEND=VULKAN flutter build linux --release
The Vulkan build requires Vulkan development headers, SPIR-V headers/tools and
glslc.
Output
WhisperResult contains detected language, combined text, timestamped segments,
optional token metadata, optional VAD regions and native timing data.
final plainText = result.text;
final json = result.toJsonString(pretty: true);
final subtitles = result.toSrt();
final selected = result.format(WhisperOutputFormat.json);
JSON uses a stable schemaVersion: 1 structure. SRT output skips empty cues and
normalizes negative or reversed timestamp ranges.
WebAssembly
Web inference runs outside the Flutter UI thread in a JavaScript Worker. The Worker, Emscripten module and WASM binary are package assets loaded automatically from:
assets/packages/flutter_whisper_ggml/assets/web/
Applications do not copy these files into web/. A model may be an HTTP(S),
Flutter asset or browser blob: URL. Browsers cannot read local paths such as
C:\models\ggml-small.bin.
The pthread runtime requires these production response headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
The minimal example includes a Netlify/Cloudflare-compatible web/_headers
file. Verify that the chosen host actually applies the headers and that remote
model responses are allowed by its CORS/cross-origin policy.
To rebuild the package Web artifacts, activate Emscripten and run one of:
.\tool\build_web_wasm.ps1 -Configuration Release
bash tool/build_web_wasm.sh Release
CI pins Emscripten 4.0.7 for reproducibility.
Examples
Minimal package example
The local example/ is an all-platform smoke application. It lets the user
select one GGML .bin model and one WAV file without bundling either file.
cd example
flutter pub get
flutter run -d windows # linux, macos, android, ios or chrome also work
On Web, selected files become temporary blob: URLs.
Full showcase
The separate
flutter_whisper_ggml_example
repository contains the full UI, model dropdown, debugging asset, optional
FFmpeg conversion, progress display, live transcript and integration tests.
Keeping it separate prevents models and FFmpeg binaries from inflating this
package.
Troubleshooting
| Symptom | Likely cause | Action |
|---|---|---|
Failed to fetch on Web |
Local filesystem path, CORS or missing isolation headers | Select files in the browser, use an allowed URL and verify COOP/COEP |
| Web Worker failed to load | Missing/stale generated Web assets | Run the Emscripten build script and rebuild the Flutter app |
| Only CPU appears | GPU backend was not compiled or its SDK/runtime is unavailable | Rebuild with CUDA/Vulkan and verify the driver/toolkit |
bad magic |
File is not a GGML Whisper model | Select an official ggml-*.bin file |
| Model reports headers only | An upstream for-tests-* fixture was selected |
Download a real model from the catalog |
| SHA-256 verification failed | Incomplete, changed or intercepted download | Retry with forceDownload: true; do not use the partial file |
| Android download has no network | INTERNET permission is absent |
Add the manifest permission shown in Quick start |
| WAV rejected | Unsupported/corrupt container or samples | Convert to mono 16 kHz PCM16 WAV |
| Repeated/hallucinated segments | Weak model, music/noise, clipping or unsuitable decoding settings | Improve the WAV, use a stronger multilingual model and tune thresholds/VAD |
| Out of memory or browser tab crash | Model exceeds available RAM/GPU/WASM limits | Use a smaller or quantized model and select CPU if GPU memory is limited |
Build and validation
Normal consumers build against the bundled source in whisper/v1.9.2.
Maintainers can explicitly test another checkout:
$env:FLUTTER_WHISPER_CPP_DIR='C:\src\whisper.cpp'
flutter build windows
Local quality checks:
flutter pub get
flutter analyze
flutter test
cd example
flutter analyze
flutter test
.github/workflows/ci.yml validates analysis, tests and the pub.dev archive,
then builds Android, Linux CPU/Vulkan, Windows CPU, Web/Emscripten, iOS Simulator,
unsigned iOS ARM64 and macOS. Real-model integration tests remain in the full
example because their fixtures and runtime are too large for the package suite.
Native packaging notes:
- Android statically links whisper.cpp/GGML into one shared library and supports 16 KiB pages;
- Linux and Windows package the stable C bridge and selected GGML backends;
- iOS and macOS compile C++17 sources through CocoaPods with Accelerate;
- Apple privacy manifests declare no tracking or collected data.
Known limitations
- Native file decoding is WAV-only by design.
- Web currently maps the common language, translation and thread controls; some advanced native decoding controls are not yet forwarded by the Worker.
- Metal, Android Vulkan and WebGPU are not implemented.
- The plugin does not provide microphone/live-stream capture.
- Downloads do not currently expose pause, resume, cancellation or a cache deletion API.
- Web has no plugin-managed persistent model cache; browser HTTP caching may still reuse a response.
- Apple platforms are CI-compiled but await physical-device validation.
- Accuracy, speed and memory depend primarily on model, quantization, audio and hardware.
License
flutter_whisper_ggml is distributed under the MIT License,
copyright (c) 2026 Ruan Dias. Bundled whisper.cpp sources retain their own
license notices.
Applications must also review the license and usage terms of each model they
distribute or download.