行业知识
flutter中使用基于flutter
2026-11-07 21:13:43
来源:互联网转载
flutter_sound_record
- 前言
- 文章案例所使用插件的版本号
- 插件安装
- 引入安卓和ios权限
- 支持的编码
- 自定义生成的文件名
- 代码
- main.dart
- audio_player.dart
- 源码地址
前言
使用flutter_sound,总是出现莫名的错误,所以改为flutter_sound_record,实现录音录制和播放
文章案例所使用插件的版本号
3.3.2
插件安装
安装方式:
命令行形式:
flutter pub add flutter_sound_record
pubspec.yaml格式 (别忘运行 pub get):
dependencies: flutter_sound_record: ^3.3.2
引入
import 'package:flutter_sound_record/flutter_sound_record.dart';
引入安卓和ios权限
Android
min SDK: 16 (29 if you use OPUS)
iOs
NSMicrophoneUsageDescriptionWe need to access to the microphone to record audio file
min SDK: 8.0 (11 if you use OPUS)
支持的编码
enum AudioEncoder { /// Will output to MPEG_4 format container AAC, /// Will output to MPEG_4 format container AAC_LD, /// Will output to MPEG_4 format container AAC_HE, /// sampling rate should be set to 8kHz /// Will output to 3GP format container on Android AMR_NB, /// sampling rate should be set to 16kHz /// Will output to 3GP format container on Android AMR_WB, /// Will output to MPEG_4 format container /// /!\ SDK 29 on Android /!\ /// /!\ SDK 11 on iOs /!\ OPUS,}自定义生成的文件名
在下面main.dart的代码中的 _start() 函数
await _audioRecorder.start();//path改为自定义的即可,其余参数根据自己需求设置await _audioRecorder.start( path: 'aFullPath/myFile.m4a', // required encoder: AudioEncoder.AAC, // by default bitRate: 128000, // by default sampleRate: 44100, // by default);
代码
main.dart
import 'dart:async';import 'package:example/audio_player.dart';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:flutter_sound_record/flutter_sound_record.dart';import 'package:just_audio/just_audio.dart' as ap;class AudioRecorder extends StatefulWidget { const AudioRecorder({required this.onStop, Key? key}) : super(key: key); final void Function(String path) onStop; @override _AudioRecorderState createState() => _AudioRecorderState(); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ObjectFlagProperty.has('onStop', onStop)); }}class _AudioRecorderState extends State { bool _isRecording = false; bool _isPaused = false; int _recordDuration = 0; Timer? _timer; Timer? _ampTimer; final FlutterSoundRecord _audioRecorder = FlutterSoundRecord(); Amplitude? _amplitude; @override void initState() { _isRecording = false; super.initState(); } @override void dispose() { _timer?.cancel(); _ampTimer?.cancel(); _audioRecorder.dispose(); super.dispose(); } @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}'), ], ], ), ), ); } Widget _buildRecordStopControl() { late Icon icon; late Color color; if (_isRecording || _isPaused) { icon = const Icon(Icons.stop, color: Colors.red, size: 30); color = Colors.red.withOpacity(0.1); } else { final ThemeData 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: () { _isRecording ? _stop() : _start(); }, ), ), ); } Widget _buildPauseResumeControl() { if (!_isRecording && !_isPaused) { return const SizedBox.shrink(); } late Icon icon; late Color color; if (!_isPaused) { icon = const Icon(Icons.pause, color: Colors.red, size: 30); color = Colors.red.withOpacity(0.1); } else { final ThemeData 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: () { _isPaused ? _resume() : _pause(); }, ), ), ); } Widget _buildText() { if (_isRecording || _isPaused) { 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 _isRecording = false); } Future _pause() async { _timer?.cancel(); _ampTimer?.cancel(); await _audioRecorder.pause(); setState(() => _isPaused = true); } Future _resume() async { _startTimer(); await _audioRecorder.resume(); setState(() => _isPaused = false); } void _startTimer() { _timer?.cancel(); _ampTimer?.cancel(); _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) { setState(() => _recordDuration++); }); _ampTimer = Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { _amplitude = await _audioRecorder.getAmplitude(); setState(() {}); }); }}void main() { runApp(const MyApp());}class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState();}class _MyAppState extends State { bool showPlayer = false; ap.AudioSource? audioSource; @override void initState() { showPlayer = false; super.initState(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: Center( child: showPlayer ? Padding( padding: const EdgeInsets.symmetric(horizontal: 25), child: AudioPlayer( source: audioSource!, onDelete: () { setState(() => showPlayer = false); }, ), ) : AudioRecorder( onStop: (String path) { setState(() { audioSource = ap.AudioSource.uri(Uri.parse(path)); showPlayer = true; }); }, ), ), ), ); } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('showPlayer', showPlayer)); properties.add(DiagnosticsProperty('audioSource', audioSource)); }}audio_player.dart
import 'dart:async';import 'package:flutter/foundation.dart';import 'package:flutter/material.dart';import 'package:just_audio/just_audio.dart' as ap;class AudioPlayer extends StatefulWidget { const AudioPlayer({ required this.source, required this.onDelete, Key? key, }) : super(key: key); /// Path from where to play recorded audio final ap.AudioSource source; /// Callback when audio file should be removed /// Setting this to null hides the delete button final VoidCallback onDelete; @override AudioPlayerState createState() => AudioPlayerState(); @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('source', source)); properties.add(ObjectFlagProperty.has('onDelete', onDelete)); }}class AudioPlayerState extends State { static const double _controlSize = 56; static const double _deleteBtnSize = 24; final ap.AudioPlayer _audioPlayer = ap.AudioPlayer(); late StreamSubscription _playerStateChangedSubscription; late StreamSubscription _durationChangedSubscription; late StreamSubscription _positionChangedSubscription; @override void initState() { _playerStateChangedSubscription = _audioPlayer.playerStateStream.listen((ap.PlayerState state) async { if (state.processingState == ap.ProcessingState.completed) { await stop(); } setState(() {}); }); _positionChangedSubscription = _audioPlayer.positionStream.listen((Duration position) => setState(() {})); _durationChangedSubscription = _audioPlayer.durationStream.listen((Duration? duration) => setState(() {})); _init(); super.initState(); } Future _init() async { await _audioPlayer.setAudioSource(widget.source); } @override void dispose() { _playerStateChangedSubscription.cancel(); _positionChangedSubscription.cancel(); _durationChangedSubscription.cancel(); _audioPlayer.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _buildControl(), _buildSlider(constraints.maxWidth), IconButton( icon: const Icon( Icons.delete, color: Color(0xFF73748D), size: _deleteBtnSize, ), onPressed: () { // ignore: always_specify_types _audioPlayer.stop().then((value) => widget.onDelete()); }, ), ], ); }, ); } Widget _buildControl() { Icon icon; Color color; if (_audioPlayer.playerState.playing) { icon = const Icon(Icons.pause, color: Colors.red, size: 30); color = Colors.red.withOpacity(0.1); } else { final ThemeData theme = Theme.of(context); icon = Icon(Icons.play_arrow, color: theme.primaryColor, size: 30); color = theme.primaryColor.withOpacity(0.1); } return ClipOval( child: Material( color: color, child: InkWell( child: SizedBox(width: _controlSize, height: _controlSize, child: icon), onTap: () { if (_audioPlayer.playerState.playing) { pause(); } else { play(); } }, ), ), ); } Widget _buildSlider(double widgetWidth) { final Duration position = _audioPlayer.position; final Duration? duration = _audioPlayer.duration; bool canSetValue = false; if (duration != null) { canSetValue = position.inMilliseconds > 0; canSetValue &= position.inMilliseconds源码地址
https://github.com/josephcrowell/flutter_sound_record/
下一篇:花雨庭服务器怎么打?