Flutter Sound System

Flutter sound recorder and player.

Getting Started

Currently only working on Android.

Usage

Android:

    FlutterSoundSystem.startRecording('File Path');
    await FlutterSoundSystem.stopRecording();

    FlutterSoundSystem.playMedia('File Path');
    FlutterSoundSystem.stopMedia();
    FlutterSoundSystem.pauseMedia();

Example:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:flutter_sound_system/flutter_sound_system.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:random_string/random_string.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  final soundRecorderAndPlayer = SoundRecorderAndPlayer();
  bool isRecording = false, isPlaying = false, isRecordingAvailable = false;
  String playStop = 'Play Recording';

  @override
  void initState() {
    super.initState();
    soundRecorderAndPlayer.init('jhgkjhgkhkjgjh');
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Sound System'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextButton(
                onPressed: () {
                  if(!isRecording && !isPlaying){
                    print('Nonu: Recording Started.');
                    soundRecorderAndPlayer.toggleRecording();
                    setState(() {
                      isRecording = true;
                      isRecordingAvailable = false;
                    });
                  }
                },
                style: ButtonStyle(
                  backgroundColor: isRecording || isPlaying ? MaterialStateProperty.all<Color>(Colors.blueGrey) : MaterialStateProperty.all<Color>(Colors.green),
                  padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(10)),
                ),
                child: const Text(
                  'Start Recording',
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                    fontSize: 18.0,
                  ),
                ),
              ),
              Container(
                margin: const EdgeInsets.only(top: 11.0, bottom: 11.0),
                child: TextButton(
                  onPressed: () {
                    if(isRecording){
                      soundRecorderAndPlayer.toggleRecording();
                      print('Nonu: Recording Done.');
                      setState(() {
                        isRecording = false;
                        isRecordingAvailable = true;
                      });
                    }
                  },
                  style: ButtonStyle(
                    backgroundColor: isRecording ? MaterialStateProperty.all<Color>(Colors.green) : MaterialStateProperty.all<Color>(Colors.blueGrey),
                    padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(10)),
                  ),
                  child: const Text(
                    'Stop Recording',
                    style: TextStyle(
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                      fontSize: 18.0,
                    ),
                  ),
                ),
              ),
              TextButton(
                onPressed: () {
                  if(isRecordingAvailable && !isRecording){
                    if(isPlaying){
                      print('Nonu: Playing Recording Stopped.');
                      setState(() {
                        isPlaying = false;
                      });
                    } else {
                      print('Nonu: Playing Recording Started.');
                      setState(() {
                        isPlaying = true;
                      });
                    }
                  }
                },
                style: ButtonStyle(
                  backgroundColor: isRecordingAvailable && !isRecording ? MaterialStateProperty.all<Color>(Colors.green) : MaterialStateProperty.all<Color>(Colors.blueGrey),
                  padding: MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(10)),
                ),
                child: const Text(
                  'Play Recording',
                  style: TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.bold,
                    fontSize: 18.0,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class SoundRecorderAndPlayer {

  bool _recording = false, _playing = false;
  String _filePath = '', _appUid = '';

  void init(String arg) async {
    _appUid = arg;

    askMicPermission();
  }

  void dispose() {
  }

  Future<void> askMicPermission() async {
    var statusMicrophone = await Permission.microphone.status;
    if (statusMicrophone != PermissionStatus.granted) {
      await Permission.microphone.request();
    }
    askStoragePermission();
  }

  Future<void> askStoragePermission() async {
    var statusStorage = await Permission.storage.status;
    if (statusStorage != PermissionStatus.granted) {
      await Permission.storage.request();
    }
  }

  bool isRecording() {
    return _recording;
  }

  Future<String> getFilePath() async {
    String fileName = 'Recording-Varta-' + _appUid + '-' + randomAlphaNumeric(10) +'.aac';
    Directory directory;
    try {
      if (Platform.isAndroid) {
        directory = (await getExternalStorageDirectory())!;
        String newPath = "";
        List<String> paths = directory.path.split("/");
        for (int x = 1; x < paths.length; x++) {
          String folder = paths[x];
          if (folder != "Android") {
            newPath += "/" + folder;
          } else {
            break;
          }
        }
        newPath = newPath + "/Vaarta/Recordings";
        directory = Directory(newPath);
      } else {
        directory = await getTemporaryDirectory();
      }
      File saveFile = File(directory.path + "/$fileName");
      if (!await directory.exists()) {
        await directory.create(recursive: true);
      }
      if (await directory.exists()) {
        return saveFile.path;
      }
      return '';
    } catch (e) {
      print(e);
      return '';
    }
  }

  Future _startRecording() async {
    _filePath = await getFilePath();
    if(_filePath != ''){
      try {
        print("Nonu: Recording started - " + _recording.toString());
        FlutterSoundSystem.startRecording(_filePath);
      } catch(e){
        print("Nonu: Recording Error - " + _recording.toString());
      }
    } else {
      _stopPlaying();
      print("Nonu: _filePath empty - " + _recording.toString());
    }
  }

  Future _stopRecording() async {
    try {
      await FlutterSoundSystem.stopRecording();
    } catch(e){
      print("Nonu: Stop Recording Error - " + e.toString());
    }
  }

  Future<String> toggleRecording() async {
    if(!_recording){
      _recording = true;
      _filePath = '';
      if (await Permission.storage.status == PermissionStatus.granted) {
        await _startRecording();
      } else {
        await Permission.storage.request();
      }
      return '';
    } else {
      _recording = false;
      await _stopRecording();
      return _filePath;
    }
  }

  bool isPlaying() {
    return _playing;
  }

  void _startPlaying(String fileString) {
    FlutterSoundSystem.playMedia(fileString);
  }

  void _stopPlaying() {
    FlutterSoundSystem.stopMedia();
  }

  Future<bool> togglePlaying(String fileString) async {
    if(_playing){
      _startPlaying(fileString);
      return true;
    } else {
      _stopPlaying();
      return false;
    }
  }
}

Libraries

flutter_sound_system