Skip to main content

waveform_recorder 2.0.0 copy "waveform_recorder: ^2.0.0" to clipboard
waveform_recorder: ^2.0.0 copied to clipboard

A Flutter package for recording audio with real-time waveform visualization, ideal for voice messaging and audio recording features in chat applications.

The waveform_recorder package provides a widget to show a waveform as an audio stream is being recorded in the style of recent AI chat packages.

Screenshot of waveform_recorder in
action

Setup #

For this package to work, you'll need to set up the underlying record package according to the setup and permission instructions.

Usage #

The main entry point for this package is the WaveformRecorder widget, which requires an instance of the WaveformRecorderController to start/stop recording. Here's an example of using the recorder to record audio and then allowing the user to play it back:

import 'dart:async';

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:waveform_recorder/waveform_recorder.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _textController = TextEditingController();
  final _waveController = WaveformRecorderController();

  @override
  void dispose() {
    _textController.dispose();
    _waveController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => MaterialApp(
        home: Scaffold(
          appBar: AppBar(title: const Text('WaveForm Example')),
          body: ListenableBuilder(
            listenable: _waveController,
            builder: (context, _) => Column(
              children: [
                Expanded(
                  child: Center(
                    child: OutlinedButton(
                      onPressed: !_waveController.isRecording &&
                              _waveController.file != null
                          ? _playRecording
                          : null,
                      child: const Text('Play'),
                    ),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: Row(
                    children: [
                      Expanded(
                        child: DecoratedBox(
                          decoration: BoxDecoration(
                            border: Border.all(color: Colors.grey),
                            borderRadius: BorderRadius.circular(8),
                          ),
                          child: _waveController.isRecording
                              ? WaveformRecorder(
                                  height: 48,
                                  controller: _waveController,
                                  onRecordingStopped: _onRecordingStopped,
                                )
                              : TextField(
                                  controller: _textController,
                                  maxLines: 1,
                                  decoration: const InputDecoration(
                                    border: InputBorder.none,
                                    contentPadding: EdgeInsets.symmetric(
                                      horizontal: 8,
                                    ),
                                  ),
                                ),
                        ),
                      ),
                      const Gap(8),
                      IconButton(
                        icon: Icon(
                          _waveController.isRecording ? Icons.stop : Icons.mic,
                        ),
                        // starting can block on an OS permission prompt
                        onPressed: _waveController.isStarting
                            ? null
                            : _toggleRecording,
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      );

  Future<void> _toggleRecording() => switch (_waveController.isRecording) {
        true => _waveController.stopRecording(),
        false => _waveController.startRecording(),
      };

  Future<void> _onRecordingStopped() async {
    final file = _waveController.file;
    if (file == null) return;

    _textController.text = ''
        '${_waveController.fileName}: '
        '${_waveController.length.inMilliseconds / 1000} seconds';

    debugPrint('Recording properties:');
    debugPrint('  uri: ${file.uri}');
    debugPrint('  fileName: ${_waveController.fileName}');
    debugPrint('  mimeType: ${_waveController.mimeType}');
  }

  Future<void> _playRecording() async {
    final file = _waveController.file;
    if (file == null) return;
    final source = file is FileSystemXFile
        ? DeviceFileSource(file.path)
        : UrlSource(file.uri);
    await AudioPlayer().play(source);
  }
}

Usage Considerations #

WaveformRecorderController.file is an XFile from the cross_file package; waveform_recorder re-exports XFile, FileSystemXFile and ScopedStorageXFile so you don't need to depend on cross_file yourself.

On every platform except the web, the recording is a temporary file on disk and file is a FileSystemXFile; it's your app's responsibility to remove this temp file when it's done with it. On the web, the recording is a blob URL and file is a ScopedStorageXFile whose uri is that URL. Use the controller's fileName and mimeType for a display/download name and content type, e.g.

  Future<void> _deleteRecording() async {
    final file = _waveController.file;
    if (file is FileSystemXFile) await File(file.path).delete();
  }

Upgrading to 2.0 #

Version 2.0 moves to cross_file 0.4, whose XFile no longer has path, mimeType or a synchronous name:

  • file.path → (file as FileSystemXFile).path on native platforms, or file.uri (the blob URL) on the web
  • file.name → controller.fileName
  • file.mimeType → controller.mimeType

It also requires Dart 3.11 / Flutter 3.41 or later.

Feedback #

Your feedback via issues or PRs is welcome!

53
likes
160
points
18.3k
downloads

Documentation

API reference

Publisher

verified publishersellsbrothers.com

Weekly Downloads

A Flutter package for recording audio with real-time waveform visualization, ideal for voice messaging and audio recording features in chat applications.

Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

cross_file, flutter, path, path_provider, record, waveform_flutter, web

More

Packages that depend on waveform_recorder