skip to Main Content

I am following this 5 minutes video to set up an audio recorder in Flutter.

When I click the ElevatedButton to start recording the audio, it does change between play and stop, and an audio file is created, but the snapshot.hasData is always false, so the Text stays 00:00 during recording. The only information I found is about setSubscriptionDuration, which I did set. I also tried flutter clean, etc. What else can it be?

I’m using Flutter 3.3.8, on macOS, flutter_sound: ^9.1.9. I’m running the app on a real iPhone XR with flutter run

I am new to flutter. I really appreciate any help you can provide!

I have

  1. StreamBuilder
        StreamBuilder<RecordingDisposition>(
          stream: recorder.onProgress,
          builder: (context, snapshot) {
            print('snapshot.hasData :${snapshot.hasData}');
            final duration =
                snapshot.hasData ? snapshot.data!.duration : Duration.zero;

            print('duration :$duration');
            String twoDigits(int n) => n.toString().padLeft(2, '0');
            final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
            final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
            return Text(
              '$twoDigitMinutes:$twoDigitSeconds',
              style: const TextStyle(
                fontSize: 20,
              ),
            );
          },
        ),
  1. ElevatedButton
        ElevatedButton(
          child: Icon(
            recorder.isRecording ? Icons.stop : Icons.mic,
            size: 20,
          ),
          onPressed: () async {
            if (recorder.isRecording) {
              await stop();
            } else {
              await record();
            }

            setState(() {});
          },
        )
  1. Initialize the recorder properly
  final recorder = FlutterSoundRecorder();
  Future<void> initRecorder() async {
    final status = await Permission.microphone.request();
    if (status != PermissionStatus.granted) {
      throw 'Microphone permission not granted';
    }
    await recorder.openRecorder();
    isRecorderReady = true;
    recorder.setSubscriptionDuration(
      const Duration(
        microseconds: 100,
      ),
    );
  }
  @override
  void initState() {
    super.initState();
    initRecorder();
  }

This is what it looks like so far:

enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    So, I found a solution, but the StreamBuilder question is not answered. Instead of using StreamBuilder, create a stateful TimerWidget that's initialized by a ValueNotifier.

    import 'dart:async';
    import 'package:flutter/material.dart';
    enum Time { start, pause, reset }
    
    class TimerController extends ValueNotifier<Time> {
      TimerController({Time time = Time.reset}) : super(time);
      void startTimer() => value = Time.start;
      void pauseTimer() => value = Time.pause;
      void resetTimer() => value = Time.reset;
    }
    
    class TimerWidget extends StatefulWidget {
      final TimerController controller;
      const TimerWidget({
        Key? key,
        required this.controller,
      }) : super(key: key);
    
      @override
      _TimerWidgetState createState() => _TimerWidgetState();
    }
    
    class _TimerWidgetState extends State<TimerWidget> {
      Duration duration = const Duration();
      Timer? timer;
    
      @override
      void initState() {
        super.initState();
        widget.controller.addListener(() {
          switch (widget.controller.value) {
            case Time.start:
              startTimer();
              break;
            case Time.pause:
              stopTimer();
              break;
            case Time.reset:
              reset();
              stopTimer();
              break;
          }
        });
      }
    
      void reset() => setState(() => duration = const Duration());
    
      void addTime() {
        const addSeconds = 1;
        setState(() {
          final seconds = duration.inSeconds + addSeconds;
          if (seconds < 0) {
            timer?.cancel();
          } else {
            duration = Duration(seconds: seconds);
          }
        });
      }
    
      void startTimer({bool resets = true}) {
        if (!mounted) return;
        timer = Timer.periodic(const Duration(seconds: 1), (_) => addTime());
      }
      void stopTimer() {
        if (!mounted) return;
        setState(() => timer?.cancel());
      }
    
      @override
      Widget build(BuildContext context) => Center(child: buildTime());
    
      Widget buildTime() {
        String twoDigits(int n) => n.toString().padLeft(2, "0");
        final twoDigitMinutes = twoDigits(duration.inMinutes.remainder(60));
        final twoDigitSeconds = twoDigits(duration.inSeconds.remainder(60));
    
        return Text(
          '$twoDigitMinutes:$twoDigitSeconds',
          style: const TextStyle(
            fontSize: 80,
            fontWeight: FontWeight.bold,
          ),
        );
      }
    }
    

  2. change the microseconds: 100, to millisecond:100 in recorder.setSubscriptionDuration

    recorder.setSubscriptionDuration(
      const Duration(milliseconds: 100),
    );
    

    like this

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search