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.

pub package License: MIT Platform


Screenshots

Ready Recording Stopped Playback & Trim
Ready state Recording in progress Recording stopped Playback and trim panel

Features

  • Drop-in widget — embed AudioRecorderWidget anywhere: a Scaffold, 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/https URL to AudioPlaybackPanel to 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 a fromColorScheme factory to match your Material theme.
  • Headless API — use AudioRecorderController directly to build a completely custom UI.
  • Feature flagsRecorderFeatures lets 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.4

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_NOTIFICATIONS and READ_PHONE_STATE are 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.

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.
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) {},
  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.
  • AudioTrimService reads and rewrites PCM data in Dart — no native code, no FFI.
  • Trimmed files are written to the same directory as the source with a _trimmed suffix 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_NOTIFICATIONS permission.
  • The service is stopped automatically when controller.stop() is called or the widget is disposed.
  • Set useForegroundService: false if 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 legacy PhoneStateListener. Requires the READ_PHONE_STATE permission. When the permission is absent, recording continues uninterrupted.
  • iOS: Listens for AVAudioSession interruption 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.