import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:record/record.dart'; import 'platform/audio_recorder_platform.dart'; class Recorder extends StatefulWidget { final void Function(String path) onStop; const Recorder({super.key, required this.onStop}); @override State createState() => _RecorderState(); } class _RecorderState extends State with AudioRecorderMixin { int _recordDuration = 0; Timer? _timer; late final AudioRecorder _audioRecorder; StreamSubscription? _recordSub; RecordState _recordState = RecordState.stop; StreamSubscription? _amplitudeSub; Amplitude? _amplitude; @override void initState() { _audioRecorder = AudioRecorder(); _recordSub = _audioRecorder.onStateChanged().listen((recordState) { _updateRecordState(recordState); }); _amplitudeSub = _audioRecorder .onAmplitudeChanged(const Duration(milliseconds: 300)) .listen((amp) { setState(() => _amplitude = amp); }); super.initState(); } Future _start() async { try { if (await _audioRecorder.hasPermission()) { const encoder = AudioEncoder.aacLc; if (!await _isEncoderSupported(encoder)) { return; } final devs = await _audioRecorder.listInputDevices(); debugPrint(devs.toString()); const config = RecordConfig(encoder: encoder, numChannels: 1); // Record to file await recordFile(_audioRecorder, config); // Record to stream // await recordStream(_audioRecorder, config); _recordDuration = 0; _startTimer(); } } catch (e) { if (kDebugMode) { print(e); } } } Future _stop() async { final path = await _audioRecorder.stop(); if (path != null) { widget.onStop(path); downloadWebData(path); } } Future _pause() => _audioRecorder.pause(); Future _resume() => _audioRecorder.resume(); void _updateRecordState(RecordState recordState) { setState(() => _recordState = recordState); switch (recordState) { case RecordState.pause: _timer?.cancel(); break; case RecordState.record: _startTimer(); break; case RecordState.stop: _timer?.cancel(); _recordDuration = 0; break; } } Future _isEncoderSupported(AudioEncoder encoder) async { final isSupported = await _audioRecorder.isEncoderSupported( encoder, ); if (!isSupported) { debugPrint('${encoder.name} is not supported on this platform.'); debugPrint('Supported encoders are:'); for (final e in AudioEncoder.values) { if (await _audioRecorder.isEncoderSupported(e)) { debugPrint('- ${encoder.name}'); } } } return isSupported; } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildRecordStopControl(), const SizedBox(width: 20), _buildPauseResumeControl(), const SizedBox(width: 20), _buildText(), ], ), if (_amplitude != null) ...[ const SizedBox(height: 40), Text('Current: ${_amplitude?.current ?? 0.0}'), Text('Max: ${_amplitude?.max ?? 0.0}'), ], ], ), ), ); } @override void dispose() { _timer?.cancel(); _recordSub?.cancel(); _amplitudeSub?.cancel(); _audioRecorder.dispose(); super.dispose(); } Widget _buildRecordStopControl() { late Icon icon; late Color color; if (_recordState != RecordState.stop) { icon = const Icon(Icons.stop, color: Colors.red, size: 30); color = Colors.red.withOpacity(0.1); } else { final theme = Theme.of(context); icon = Icon(Icons.mic, color: theme.primaryColor, size: 30); color = theme.primaryColor.withOpacity(0.1); } return ClipOval( child: Material( color: color, child: InkWell( child: SizedBox(width: 56, height: 56, child: icon), onTap: () { (_recordState != RecordState.stop) ? _stop() : _start(); }, ), ), ); } Widget _buildPauseResumeControl() { if (_recordState == RecordState.stop) { return const SizedBox.shrink(); } late Icon icon; late Color color; if (_recordState == RecordState.record) { icon = const Icon(Icons.pause, color: Colors.red, size: 30); color = Colors.red.withOpacity(0.1); } else { final theme = Theme.of(context); icon = const Icon(Icons.play_arrow, color: Colors.red, size: 30); color = theme.primaryColor.withOpacity(0.1); } return ClipOval( child: Material( color: color, child: InkWell( child: SizedBox(width: 56, height: 56, child: icon), onTap: () { (_recordState == RecordState.pause) ? _resume() : _pause(); }, ), ), ); } Widget _buildText() { if (_recordState != RecordState.stop) { return _buildTimer(); } return const Text("Waiting to record"); } Widget _buildTimer() { final String minutes = _formatNumber(_recordDuration ~/ 60); final String seconds = _formatNumber(_recordDuration % 60); return Text( '$minutes : $seconds', style: const TextStyle(color: Colors.red), ); } String _formatNumber(int number) { String numberStr = number.toString(); if (number < 10) { numberStr = '0$numberStr'; } return numberStr; } void _startTimer() { _timer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) { setState(() => _recordDuration++); }); } }