Plugin introduction:
flutter_sound
This plug-in can realize iOS和Android
the recording and playback functions of the platform. That is, you can play local audio files and remote URL files. Here I will introduce the usage of this plugin and how to solve some common problems encountered.
-
flutter_sound supports multiple recording formats
-
flutter_sound supports multiple playback formats
-
flutter_sound supports audio amplitude size
Plugin info:
Plugin address: github.com/ryanheise/a…
Plugin version: 9.2.9
Preparation before using the plugin
Set microphone permission description
- iOS: You need to add permissions to the info.plist file
<key>NSMicrophoneUsageDescription</key>
<string>描述你使用麦克风用来干嘛</string>
复制代码
注意:还需要在Podfile文件中配置
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
## dart: PermissionGroup.microphone
'PERMISSION_MICROPHONE=1',
]
end
end
end
复制代码
You also need to add a libc++.tbd
library to the iOS project, the specific path
- Android: requires setup
AndroidManifest.xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
复制代码
The following plugins are also used here
Permission management plugin permission_handler
Plugin info:permission_handler: ^9.2.0
Plugin address: pub.flutter-io.cn/packages/pe…
Audio hardware configuration plugin audio_session
Plugin info:audio_session: ^0.1.6
Plugin address: github.com/ryanheise/a…
animation plugin
Plugin info:lottie: 1.2.1
Plugin address: pub.flutter-io.cn/packages/fl…
Commonly used methods
Common methods of recording
Initialize the recording object
FlutterSoundRecorder recorderModule = FlutterSoundRecorder();
复制代码
Turn on recording
Future<void> init() async {
//开启录音
await recorderModule.openRecorder();
//设置订阅计时器
await recorderModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//初始化日期插件
await initializeDateFormatting();
}
复制代码
Microphone permissions
Future<bool> getPermissionStatus() async {
Permission permission = Permission.microphone;
//granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示
PermissionStatus status = await permission.status;
if (status.isGranted) {
return true;
} else if (status.isDenied) {
requestPermission(permission);
} else if (status.isPermanentlyDenied) {
openAppSettings();
} else if (status.isRestricted) {
requestPermission(permission);
} else {}
return false;
}
///申请权限
void requestPermission(Permission permission) async {
PermissionStatus status = await permission.request();
if (status.isPermanentlyDenied) {
openAppSettings();
}
}
复制代码
start recording
/// 开始录音
_startRecorder() async {
try {
//获取麦克风权限
await getPermissionStatus().then((value) async {
if (!value) {
return;
}
//用户允许使用麦克风之后开始录音
Directory tempDir = await getTemporaryDirectory();
var time = DateTime.now().millisecondsSinceEpoch;
String path = '${tempDir.path}/$time${ext[Codec.aacADTS.index]}';
//这里我录制的是aac格式的,还有其他格式
await recorderModule.startRecorder(
toFile: path,
codec: Codec.aacADTS,
bitRate: 8000,
numChannels: 1,
sampleRate: 8000,
);
/// 监听录音
_recorderSubscription = recorderModule.onProgress!.listen((e) {
var date = DateTime.fromMillisecondsSinceEpoch(
e.duration.inMilliseconds,
isUtc: true);
var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
//设置了最大录音时长
if (date.second >= _maxLength) {
_stopRecorder();
return;
}
setState(() {
//更新录音时长
_recordText = txt.substring(1, 5);
});
});
setState(() {
//更新录音状态和录音文件路径
_state = RecordPlayState.recording;
_path = path;
});
});
} catch (err) {
setState(() {
_stopRecorder();
_state = RecordPlayState.record;
_cancelRecorderSubscriptions();
});
}
}
复制代码
end recording
/// 结束录音
_stopRecorder() async {
try {
await recorderModule.stopRecorder();
_cancelRecorderSubscriptions();
// _getDuration();
} catch (err) {}
setState(() {
_state = RecordPlayState.record;
});
}
///销毁录音
void dispose() {
super.dispose();
_cancelRecorderSubscriptions();
_releaseFlauto();
}
/// 取消录音监听
void _cancelRecorderSubscriptions() {
if (_recorderSubscription != null) {
_recorderSubscription!.cancel();
_recorderSubscription = null;
}
}
/// 释放录音
Future<void> _releaseFlauto() async {
try {
await recorderModule.closeRecorder();
} catch (e) {}
}
/// 判断文件是否存在
Future<bool> _fileExists(String path) async {
return await File(path).exists();
}
复制代码
Play common methods
Initialize the player
FlutterSoundPlayer playerModule = FlutterSoundPlayer();
复制代码
initialization operation
init() async {
await playerModule.closePlayer();
await playerModule.openPlayer();
await playerModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//这块是设置音频,暂时没用到可以不用设置
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration(
avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
avAudioSessionCategoryOptions:
AVAudioSessionCategoryOptions.allowBluetooth |
AVAudioSessionCategoryOptions.defaultToSpeaker,
avAudioSessionMode: AVAudioSessionMode.spokenAudio,
avAudioSessionRouteSharingPolicy:
AVAudioSessionRouteSharingPolicy.defaultPolicy,
avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
androidAudioAttributes: const AndroidAudioAttributes(
contentType: AndroidAudioContentType.speech,
flags: AndroidAudioFlags.none,
usage: AndroidAudioUsage.voiceCommunication,
),
androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
androidWillPauseWhenDucked: true,
));
}
复制代码
Start playing
///开始播放,这里做了一个播放状态的回调
void startPlayer(PlayStateBack callBack) async {
try {
if (path.contains('http')) {
await playerModule.startPlayer(
fromURI: path,
codec: Codec.mp3,
sampleRate: 44000,
whenFinished: () {
stopPlayer();
callBack(0);
});
} else {
//判断文件是否存在
if (await _fileExists(path)) {
if (playerModule.isPlaying) {
playerModule.stopPlayer();
}
await playerModule.startPlayer(
fromURI: path,
codec: Codec.aacADTS,
sampleRate: 44000,
whenFinished: () {
stopPlayer();
callBack(0);
});
} else {}
}
//监听播放进度
_playerSubscription = playerModule.onProgress!.listen((e) {});
callBack(1);
} catch (err) {
callBack(0);
}
}
复制代码
end playback
/// 结束播放
void stopPlayer() async {
try {
await playerModule.stopPlayer();
cancelPlayerSubscriptions();
} catch (err) {}
}
/// 取消播放监听
void cancelPlayerSubscriptions() {
if (_playerSubscription != null) {
_playerSubscription!.cancel();
_playerSubscription = null;
}
}
///获取播放状态
Future<PlayerState> getPlayState() async {
return await playerModule.getPlayerState();
}
/// 释放播放器
void releaseFlauto() async {
try {
await playerModule.closePlayer();
} catch (e) {
print(e);
}
}
/// 判断文件是否存在
Future<bool> _fileExists(String path) async {
return await File(path).exists();
}
复制代码
Animation implementation
In the process of recording and playback, it is inevitable to use animation. Here I will tell you how to load it.gif和动画文件
Loading animated gifs
Visibility(
visible: (item.playing.value == 1) ? true : false,
child: Image.asset('assets/-comm/comm_audio_paly.gif', width: 20, height: 20,),
replacement: const Image(
image: AssetImage('assets/-comm/comm_audio_icon.png'),
width: 20,
height: 20,
),
)
复制代码
Load animation file
Lottie.asset('assets/-comm/record_audio_animation.json',
height: 25,
width: ScreenAdapter.screenWidth() -ScreenAdapter.width(160),
animate: true)
复制代码
upload files
Upload audio files
var map = {
"file": MultipartFile.fromBytes(
await File.fromUri(Uri(path: path)).readAsBytes(),
filename: "$fileName.mp3",
contentType: MediaType.parse("audio/mp3"))
};
复制代码
Summarize
The above describes how to record, how to play local and remote audio files, how to implement animation, and how to upload audio files after recording. These are the problems we usually encounter when using this function. The problems encountered in the process of use are also listed, I hope to help you.