flutter_recorder setup
flutter_recorder is a cross-platform Flutter audio capture plugin powered by the miniaudio C library. It operates via Dart FFI on mobile/desktop and WebAssembly (WASM) on web. All capture functionality is coordinated through the singleton Recorder.instance (import 'package:flutter_recorder/flutter_recorder.dart'), which must be initialized with init() before capture starts, and cleaned up with deinit() when finished.
Minimal example
import 'package:flutter/foundation.dart';
import 'package:flutter_recorder/flutter_recorder.dart';
import 'package:permission_handler/permission_handler.dart';
Future<void> main() async {
// 1. Request microphone permission on mobile/desktop platforms
if (!kIsWeb) {
final status = await Permission.microphone.request();
if (!status.isGranted) {
return; // Handle permission denied
}
}
// 2. Optional: inspect available capture devices (safe pre-init)
final devices = Recorder.instance.listCaptureDevices();
// 3. Initialize capture engine
await Recorder.instance.init(
format: PCMFormat.f32le, // f32le required for visualization & silence detection
sampleRate: 22050,
channels: RecorderChannels.mono,
// Android hardware DSP preset
androidInputPreset: AndroidInputPreset.voiceCommunication,
// iOS AVAudioSession preset (Apple hardware VoiceProcessingIO / Measurement)
iosInputPreset: IosInputPreset.voiceCommunication,
// Web Audio preprocessing constraints (echoCancellation, AGC, noiseSuppression)
webInputPreset: WebInputPreset.unprocessed,
// deviceID: devices.firstWhere((d) => d.isDefault).id,
);
// 4. Start hardware capture device
Recorder.instance.start();
// 5. On shutdown / dispose:
Recorder.instance.deinit(); // stops capture and frees native resources
}
Adding the package
flutter pub add flutter_recorder
Native C++ sources are built automatically using Dart build hooks and native toolchains. No manual CMake or CocoaPods configuration is required.
Platform setup & presets
Android
Add the audio recording permission to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
Android supports hardware capture presets via AndroidInputPreset:
await Recorder.instance.init(
androidInputPreset: AndroidInputPreset.voiceCommunication,
);
Available presets:
AndroidInputPreset.generic: Standard Android capture preset.AndroidInputPreset.camcorder: Tuned for video recording directionality.AndroidInputPreset.voiceRecognition: Optimized for ASR/speech-to-text with minimal AGC/filtering.AndroidInputPreset.voiceCommunication: Optimized for VoIP/calls with hardware AEC/NS.AndroidInputPreset.unprocessed: Clean, raw audio bypassing OEM DSP.
Note: Android 15 16k page sizes are supported out of the box.
iOS & macOS
Add the microphone usage description to ios/Runner/Info.plist and macos/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>We need access to your microphone to record audio.</string>
iOS (IosInputPreset)
Configures the system AVAudioSession directly from native code without needing external packages:
await Recorder.instance.init(
iosInputPreset: IosInputPreset.voiceCommunication,
);
Available presets:
IosInputPreset.generic: StandardAVAudioSessionCategoryPlayAndRecordwith default system routing.IosInputPreset.voiceCommunication: EnablesAVAudioSessionModeVoiceChat+ Apple hardwareVoiceProcessingIO(hardware AEC and AGC).IosInputPreset.videoChat: EnablesAVAudioSessionModeVideoChatoptimized for video calls and speakerphone.IosInputPreset.speechRecognition: EnablesAVAudioSessionModeMeasurementwith minimal gain distortion for speech-to-text.IosInputPreset.unprocessed: EnablesAVAudioSessionModeMeasurementwith flat frequency response and zero gain coloring for raw DSP analysis.- If omitted (
null), the activeAVAudioSessionis left untouched, preserving external session management (e.g.package:audio_session).
macOS
On macOS, CoreAudio HAL captures raw, unprocessed audio from the selected input device by default. System-wide "Voice Isolation" and "Wide Spectrum" Mic Modes in macOS Sonoma/Sequoia can be selected by the user in the macOS menu bar / Control Center.
Enable the "Audio input" capability in Xcode or add the entitlement to macos/Runner/*.entitlements:
<key>com.apple.security.device.audio-input</key>
<true/>
Web
Add the following script tags inside the <head> of web/index.html:
<script src="assets/packages/flutter_recorder/web/libflutter_recorder_plugin.js" defer></script>
<script src="assets/packages/flutter_recorder/web/init_recorder_module.dart.js" defer></script>
Web (WebInputPreset)
Configures browser getUserMedia preprocessing constraints at stream creation time:
await Recorder.instance.init(
webInputPreset: WebInputPreset.unprocessed, // default
);
Available presets:
WebInputPreset.unprocessed(Default):{echoCancellation: false, autoGainControl: false, noiseSuppression: false}. Prevents browser "volume pumping" (rapid gain oscillations) so clean audio reaches your app.WebInputPreset.voiceCommunication:{echoCancellation: true, autoGainControl: true, noiseSuppression: true}.WebInputPreset.voiceRecognition:{echoCancellation: false, autoGainControl: true, noiseSuppression: true}.WebInputPreset.noiseSuppression:{noiseSuppression: true, echoCancellation: false, autoGainControl: false}.WebInputPreset.echoCancellation:{echoCancellation: true, autoGainControl: false, noiseSuppression: false}.
To run web apps locally during development:
flutter run -d chrome --wasm --web-browser-flag '--disable-web-security' -t lib/main.dart --release
Web note: On web, initialize Recorder.instance.init() and wait for user interaction to grant microphone permissions before calling Recorder.instance.start().
Linux
Linux uses ALSA and GStreamer:
- Install development libraries:
sudo apt-get install libasound2-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev - Avoid installing Flutter via Snap, as Snap sandboxing can prevent native audio plugins from linking to ALSA/GStreamer.
Audio Session Coordination (audio_session) & flutter_soloud
When using flutter_recorder alongside audio playback engines like flutter_soloud or speech synthesis, coordinate the shared audio session using package:audio_session:
import 'package:audio_session/audio_session.dart';
import 'package:flutter_recorder/flutter_recorder.dart';
import 'package:flutter_soloud/flutter_soloud.dart';
Future<void> setupAudioStack() async {
// 1. Configure audio_session for simultaneous playback and recording
final session = await AudioSession.instance;
await session.configure(
AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.voiceChat,
androidAudioAttributes: const AndroidAudioAttributes(
usage: AndroidAudioUsage.voiceCommunication,
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
),
androidWillPauseWhenDucked: false,
),
);
await session.setActive(true);
// 2. Initialize SoLoud for playback
await SoLoud.instance.init(
channels: Channels.mono,
sampleRate: 22050,
);
// 3. Initialize Recorder (leave iosInputPreset null so audio_session settings are preserved)
await Recorder.instance.init(
format: PCMFormat.f32le,
sampleRate: 22050,
channels: RecorderChannels.mono,
);
}
The API Shape
All methods live on Recorder.instance (import 'package:flutter_recorder/flutter_recorder.dart'):
Future<void> init({int deviceID = -1, PCMFormat format = PCMFormat.s16le, int sampleRate = 22050, RecorderChannels channels = RecorderChannels.mono, AndroidInputPreset? androidInputPreset, IosInputPreset? iosInputPreset, WebInputPreset? webInputPreset}): Initializes the audio capture device. ThrowsRecorderInitializeFailedExceptionon failure.void start(): Starts audio capture. ThrowsRecorderNotInitializedExceptionorRecorderFailedToStartDeviceException.void stop(): Stops audio capture without deinitializing the engine.void deinit(): Stops capture and disposes all native device resources.bool isDeviceInitialized()/bool get isInitialized: Checks whether the capture engine is ready.bool isDeviceStarted()/bool get isStarted: Checks whether the microphone capture is active.List<CaptureDevice> listCaptureDevices(): Enumerates available input devices. ReturnsCaptureDevice(name, isDefault, id). Safe to call beforeinit().Stream<RecorderDeviceNotification> get deviceNotificationEvents: Broadcast stream of native device notifications and lifecycle events (started,stopped,rerouted,interruptionBegan,interruptionEnded,unlocked).void setLoopback({required bool enable})/bool isLoopbackEnabled(): Enables low-latency (< 15ms) native duplex loopback (mic routed directly to speakers/headphones).void feedPlaybackData(Uint8List data, {PCMFormat format = PCMFormat.f32le, RecorderChannels channels = RecorderChannels.mono}): Feeds far-end reference playback audio (e.g. fromflutter_soloud) into the recorder for SpeexDSP Acoustic Echo Cancellation.double getVolumeDb(): Returns current RMS volume level in dB[-100, 0]. RequiresPCMFormat.f32le.
Logging
flutter_recorder logs all diagnostic and error messages via standard package:logging under the logger name flutter_recorder.Recorder:
import 'dart:developer' as dev;
import 'package:flutter/foundation.dart';
import 'package:logging/logging.dart';
void setupLogging() {
Logger.root.level = kDebugMode ? Level.FINE : Level.INFO;
Logger.root.onRecord.listen((record) {
dev.log(
record.message,
name: record.loggerName,
level: record.level.value,
error: record.error,
stackTrace: record.stackTrace,
);
});
}
Traps & Gotchas
- Always initialize with
PCMFormat.f32leif you need Visualization or Silence Detection: Features likeaudioVisualizationEvents,getFft(),getWave(),getVolumeDb(), andsetSilenceDetection()requirePCMFormat.f32le. - Calling
init()while already initialized deinitializes first: Guard withif (!Recorder.instance.isInitialized)if you don't intend to reset the device. - Microphone Permissions are runtime requirements: Calling
init()orstart()without user permission will throw exceptions on mobile. - Web requires the two
<script>tags inweb/index.html: Missing tags lead to WASM initialization errors. - File paths on Web are ignored: When calling
startRecording(), the browser automatically prompts for file download uponstopRecording().
More depth
- references/web.md — WebAssembly module, web worker architecture, and browser permission nuances.
- references/platform-permissions.md — Permission and manifest configurations across Android, iOS, macOS, Windows, and Linux.
Keeping this skill current
This skill ships inside the flutter_recorder package. To check if an updated version is available, run:
dart run flutter_recorder:skills --check