flutter_audio_recorder_kit
A drop-in audio recording kit for Flutter — themeable recorder UI with live waveform, playback panel, remote URL streaming, WAV trimming, foreground-service recording, and auto-pause-on-call for Android. Every feature is individually toggleable.
Screenshots
| Ready | Recording | Stopped | Playback & Trim |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
Features
- Drop-in widget — embed
AudioRecorderWidgetanywhere: aScaffold, a bottom sheet, a tab, or a dialog. It is a plain widget, not a screen. - Live waveform — animated bar visualization driven by real-time microphone amplitude.
- Elapsed timer — centisecond-precision elapsed time display with pause-aware accumulation.
- Status pill — READY / RECORDING / PAUSED indicator that updates automatically.
- Playback panel — built-in play/pause button and seek slider for the recorded, uploaded, or remotely streamed file.
- Remote URL streaming — pass any
http/httpsURL toAudioPlaybackPanelto stream audio directly, no local download required. - WAV trimming — pure-Dart in-app start/end trim with a range slider, no native code required.
- File upload — optional "Upload audio" button to load an existing file from the device.
- Format selector — WAV or AAC (.m4a) switchable from within the UI before recording starts.
- Foreground-service recording (Android) — keeps the recording alive in the background with a persistent notification.
- Auto-pause on call — pauses automatically when a phone call (Android) or audio interruption (iOS) begins, then resumes when it ends.
- Full theming — every color is overridable via
AudioRecorderTheme, with afromColorSchemefactory to match your Material theme. - Headless API — use
AudioRecorderControllerdirectly to build a completely custom UI. - Feature flags —
RecorderFeatureslets you toggle recording, playback, trimming, and upload independently per widget instance.
Platform Support
| Platform | Minimum version | Notes |
|---|---|---|
| Android | API 24 (Android 7.0) | Foreground service + telephony auto-pause |
| iOS | iOS 13 | Background audio mode + interruption auto-pause |
Installation
Add the package to your pubspec.yaml:
dependencies:
flutter_audio_recorder_kit: ^0.1.5
Then run:
flutter pub get
Android Setup
1. Permissions in AndroidManifest.xml
The package automatically merges RECORD_AUDIO, FOREGROUND_SERVICE, and FOREGROUND_SERVICE_MICROPHONE into your app's manifest. You must declare the privacy-sensitive permissions yourself:
<!-- Required for recording -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Required on Android 13+ if useForegroundService is true -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<!-- Required for auto-pause-on-call (autoPauseOnCall: true) -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Note:
POST_NOTIFICATIONSandREAD_PHONE_STATEare intentionally not merged automatically because they are privacy-sensitive optional features. Only add them if you use those features.
2. Request permissions at runtime
Use permission_handler (already a transitive dependency) or any other package to request RECORD_AUDIO, and optionally POST_NOTIFICATIONS / READ_PHONE_STATE, before starting a recording.
iOS Setup
1. Info.plist
Add a microphone usage description:
<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access to record audio.</string>
2. Background audio capability
To keep recording running when the app moves to the background, enable the Background Modes capability in Xcode and check Audio, AirPlay, and Picture in Picture.
Or add directly to ios/Runner/Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
Quick Start
import 'package:flutter_audio_recorder_kit/flutter_audio_recorder_kit.dart';
Scaffold(
body: AudioRecorderWidget(
onRecordingComplete: (path) {
print('Saved to: $path');
},
),
)
That's it. The widget handles permissions, the foreground service, waveform visualization, playback, and trimming out of the box.
Usage
Basic — default feature set
Recording + playback + trimming, no upload button:
AudioRecorderWidget(
onRecordingComplete: (path) => print('Recorded: $path'),
)
All features enabled
AudioRecorderWidget(
features: RecorderFeatures.all, // recording + playback + trimming + upload
onRecordingComplete: (path) => print('Recorded: $path'),
onUploadComplete: (path) => print('Uploaded: $path'),
onTrimComplete: (path) => print('Trimmed: $path'),
)
Record only (no result panel)
AudioRecorderWidget(
features: RecorderFeatures.recordOnly,
onRecordingComplete: (path) => handleAudio(path),
)
Custom feature combination
AudioRecorderWidget(
features: const RecorderFeatures(
recording: true,
playback: true,
trimming: false,
upload: false,
),
)
Playback + upload of existing files
AudioRecorderWidget(
features: RecorderFeatures.playbackOnly,
onUploadComplete: (path) => handleAudio(path),
)
Custom recording configuration
AudioRecorderWidget(
config: const RecordingConfig(
format: RecordingFormat.aacM4a,
sampleRate: 48000,
bitRate: 192000,
numChannels: 1,
notificationTitle: 'Voice memo',
notificationText: 'Recording in progress…',
useForegroundService: true,
autoPauseOnCall: true,
),
onRecordingComplete: (path) => handleAudio(path),
)
Match your app's Material theme
AudioRecorderWidget(
theme: AudioRecorderTheme.fromColorScheme(Theme.of(context).colorScheme),
onRecordingComplete: (path) => handleAudio(path),
)
Transparent background (blend into your own background)
AudioRecorderWidget(
theme: AudioRecorderTheme.fallback.copyWith(
backgroundStart: Colors.transparent,
backgroundEnd: Colors.transparent,
),
)
Hide the built-in title bar (use your own AppBar)
Scaffold(
appBar: AppBar(title: const Text('Record')),
body: AudioRecorderWidget(
showAppBarRow: false,
onRecordingComplete: (path) => handleAudio(path),
),
)
Standalone playback panel
To play back (and trim) a file you already have, without the recording controls:
AudioPlaybackPanel(
filePath: '/path/to/audio.wav',
enableTrim: true,
onTrimComplete: (path) => print('Trimmed: $path'),
)
Play audio from a remote URL
Pass any http/https URL to filePath — the panel streams it directly via
just_audio without downloading first. Trim controls are hidden automatically
for remote sources.
AudioPlaybackPanel(
filePath: 'https://your-api.com/audio/recording.mp3',
)
Headless controller (custom UI)
final controller = AudioRecorderController(
config: const RecordingConfig(format: RecordingFormat.wav),
);
// Listen to amplitude for a custom waveform
controller.amplitudeStream.listen((db) => updateWaveform(db));
// Listen for call interruptions
controller.callInterruptionStream.listen((interrupted) {
if (interrupted) print('Call started – recording paused');
else print('Call ended – recording resumed');
});
await controller.start();
// … later …
final path = await controller.stop();
await controller.dispose();
API Reference
AudioRecorderWidget
The main drop-in widget.
| Parameter | Type | Default | Description |
|---|---|---|---|
controller |
AudioRecorderController? |
null |
Provide your own controller; the widget creates one from config if omitted. |
config |
RecordingConfig |
RecordingConfig() |
Recording settings (format, sample rate, foreground service, etc.). Ignored when controller is provided. |
theme |
AudioRecorderTheme? |
AudioRecorderTheme.fallback |
Color theme. |
features |
RecorderFeatures |
RecorderFeatures() |
Which capabilities to show. |
showAppBarRow |
bool |
true |
Show the built-in title / subtitle / format-selector row. |
showFormatSelector |
bool |
true |
Show the WAV / AAC format pill. |
title |
String |
'Audio Recorder' |
Title in the top row. |
subtitle |
String |
'Record · Play · Trim' |
Subtitle in the top row. |
onRecordingComplete |
ValueChanged<String>? |
— | Called with the file path when a recording finishes. |
onUploadComplete |
ValueChanged<String>? |
— | Called with the file path when the user picks a file. |
onTrimComplete |
ValueChanged<String>? |
— | Called with the new file path after a successful trim. |
onError |
void Function(Object, StackTrace)? |
— | Called when any operation fails. |
onCallInterruption |
ValueChanged<bool>? |
— | Called with true on interruption start, false on resume. |
RecorderFeatures
Controls which parts of the UI are visible.
| Flag | Default | Description |
|---|---|---|
recording |
true |
Record/pause/resume/stop controls, waveform, timer. |
playback |
true |
Play/pause button and seek slider after recording. |
trimming |
true |
WAV trim slider and Trim & save button. |
upload |
false |
"Upload audio" button to pick an existing file. |
Presets:
| Preset | recording | playback | trimming | upload |
|---|---|---|---|---|
RecorderFeatures() |
✓ | ✓ | ✓ | — |
RecorderFeatures.all |
✓ | ✓ | ✓ | ✓ |
RecorderFeatures.recordOnly |
✓ | — | — | — |
RecorderFeatures.recordAndTrim |
✓ | — | ✓ | — |
RecorderFeatures.playbackOnly |
— | ✓ | — | ✓ |
RecordingConfig
| Field | Type | Default | Description |
|---|---|---|---|
format |
RecordingFormat |
.aacM4a |
Output format: wav or aacM4a. |
sampleRate |
int? |
null |
Sample rate in Hz. When null, resolved from format: 44100 for aacM4a, 16000 for wav. |
bitRate |
int |
128000 |
Bit rate for compressed formats (bps). If you lower sampleRate for aacM4a, lower this too (e.g. 32000-64000) — a high bit rate at a low sample rate can produce unplayable AAC files on iOS. |
numChannels |
int |
1 |
1 = mono, 2 = stereo. |
outputDirectory |
String? |
null |
Absolute path for output files. Defaults to <documents>/recordings. |
fileNameBuilder |
String Function()? |
null |
Custom file name (no extension). Defaults to recording_<timestamp>. |
useForegroundService |
bool |
true |
Android foreground service with background recording. |
autoPauseOnCall |
bool |
true |
Auto-pause on phone call / audio interruption. |
notificationTitle |
String |
'Recording audio' |
Android notification title. |
notificationText |
String |
'Audio recording is running in the background' |
Android notification body. |
AudioRecorderTheme
All colors have sensible defaults (dark, purple-accented). Use copyWith to override individual fields, or fromColorScheme to derive the whole theme from your app's ColorScheme.
| Field | Description |
|---|---|
primary |
Main accent: idle record button, play button, seek slider. |
primaryLight / primaryDark |
Gradient shades of primary. |
recording / recordingLight |
Record button and RECORDING pill color. |
waveformActive / waveformActiveLight |
Waveform bars while recording. |
waveformIdle |
Waveform bars while idle. |
success |
READY pill, resume button, trim slider and button. |
warning |
PAUSED pill. |
accent |
Upload button. |
backgroundStart / backgroundEnd |
Radial gradient background. Set both to Colors.transparent to disable. |
surface |
Format dropdown surface color. |
textPrimary |
Main text and icon color. |
timerTextStyle |
Optional override for the large elapsed-time readout. |
AudioRecorderController
Headless recording API for custom UIs.
| Method / Property | Description |
|---|---|
start() |
Request microphone permission and start recording. |
pause() |
Pause the current recording. |
resume() |
Resume a paused recording. |
stop() → Future<String?> |
Stop and finalise; returns the file path. |
dispose() |
Release all resources. Call when done. |
config |
Read/write the RecordingConfig (only write between recordings). |
amplitudeStream |
Stream<double> — amplitude in dBFS (~120 ms interval). Feed to WaveformWidget. |
callInterruptionStream |
Stream<bool> — true on interruption start, false on resume. |
AudioPlaybackPanel
Standalone playback and trim panel. Accepts a local file path or a remote
http/https URL. Trim controls are hidden automatically for remote URLs.
The panel's subtitle shows the file size and duration once loaded (e.g.
3.4 MB · 01:24).
| Parameter | Type | Default | Description |
|---|---|---|---|
filePath |
String |
required | Absolute local path or http/https URL of the audio to load. |
theme |
AudioRecorderTheme |
AudioRecorderTheme.fallback |
Color theme. |
showPlayer |
bool |
true |
Show the play/pause button and seek slider. |
enableTrim |
bool |
true |
Show the WAV trim slider and Trim & save button (local WAV files only). |
onTrimComplete |
ValueChanged<String>? |
— | Called with the new file path after a successful trim. |
onMetadataLoaded |
void Function(Duration, int?)? |
— | Called after the audio loads, with its duration and file size in bytes (null size for remote URLs). |
onError |
void Function(Object, StackTrace)? |
— | Called when loading or trimming fails. |
// Local file with trimming
AudioPlaybackPanel(
filePath: '/path/to/audio.wav',
enableTrim: true,
onTrimComplete: (path) {},
onMetadataLoaded: (duration, fileSizeBytes) {},
onError: (e, st) {},
)
// Remote URL (trim controls hidden automatically)
AudioPlaybackPanel(
filePath: 'https://your-api.com/audio/recording.mp3',
)
AudioTrimService
Pure-Dart PCM WAV trimmer. Works on WAV files only.
final trimmed = await AudioTrimService.trim(
sourcePath: '/path/to/original.wav',
startSeconds: 1.5,
endSeconds: 8.0,
);
print('Trimmed file: $trimmed');
WaveformWidget
Standalone animated bar waveform. Accepts any List<double> of amplitude samples (−∞ to 0 dBFS; the widget normalises them automatically).
WaveformWidget(
samples: myAmplitudeSamples,
isRecording: true,
theme: myTheme,
)
Trimming Notes
- Trimming is only supported for local WAV files. The trim controls are hidden automatically for
.m4a, other formats, and remote URLs. AudioTrimServicereads and rewrites PCM data in Dart — no native code, no FFI.- Trimmed files are written to the same directory as the source with a
_trimmedsuffix appended to the file name.
Foreground Service (Android)
When RecordingConfig.useForegroundService is true (the default), the plugin starts an Android foreground service before recording begins. This keeps the recording alive when the app is backgrounded.
- The service shows a persistent notification. On Android 13+ the notification is only visible if the app has the
POST_NOTIFICATIONSpermission. - The service is stopped automatically when
controller.stop()is called or the widget is disposed. - Set
useForegroundService: falseif you only need in-foreground recording or are building a form where the user will not leave the app.
Auto-Pause on Call
When RecordingConfig.autoPauseOnCall is true (the default):
- Android: Listens for telephony state changes via
TelephonyCallback(API 31+) or the legacyPhoneStateListener. Requires theREAD_PHONE_STATEpermission. When the permission is absent, recording continues uninterrupted. - iOS: Listens for
AVAudioSessioninterruption events (Siri, phone calls, alarms). No extra permission required.
The onCallInterruption callback on AudioRecorderWidget (and callInterruptionStream on AudioRecorderController) fire for every interruption event regardless of whether the auto-pause actually changes recording state.
License
MIT License
Copyright (c) 2026 Midhun Murali
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Libraries
- flutter_audio_recorder_kit
- A drop-in audio recording kit for Flutter.



