基于flutter?sound插件實(shí)現(xiàn)錄音與播放功能
插件介紹:
flutter_sound這個(gè)插件可以實(shí)現(xiàn)iOS和Android平臺(tái)的錄音和播放功能。即可以播放本地音頻文件,也可以播放遠(yuǎn)程URL文件。在這里我講介紹這個(gè)插件的用法以及碰到的一些常見(jiàn)問(wèn)題如何解決。
flutter_sound支持多種錄音格式
flutter_sound支持多種播放格式
flutter_sound支持音頻振幅大小
插件信息:
插件版本:9.2.9
插件使用前的準(zhǔn)備工作
設(shè)置麥克風(fēng)權(quán)限描述
- iOS:需要在info.plist文件添加一下權(quán)限
<key>NSMicrophoneUsageDescription</key> <string>描述你使用麥克風(fēng)用來(lái)干嘛</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還需要在iOS工程中增加libc++.tbd庫(kù),具體路徑

- Android:需要設(shè)置
AndroidManifest.xml
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

這里還用了下面幾個(gè)插件
權(quán)限管理插件 permission_handler
插件信息:permission_handler: ^9.2.0
插件地址:pub.flutter-io.cn/packages/pe…
音頻硬件配置插件 audio_session
插件信息:audio_session: ^0.1.6
動(dòng)畫(huà)插件
插件信息:lottie: 1.2.1
插件地址:pub.flutter-io.cn/packages/fl…
常用的方法
錄音常見(jiàn)的方法
初始化錄音對(duì)象
FlutterSoundRecorder recorderModule = FlutterSoundRecorder();
開(kāi)啟錄音
Future<void> init() async {
//開(kāi)啟錄音
await recorderModule.openRecorder();
//設(shè)置訂閱計(jì)時(shí)器
await recorderModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//初始化日期插件
await initializeDateFormatting();
}麥克風(fēng)權(quán)限
Future<bool> getPermissionStatus() async {
Permission permission = Permission.microphone;
//granted 通過(guò),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;
}
///申請(qǐng)權(quán)限
void requestPermission(Permission permission) async {
PermissionStatus status = await permission.request();
if (status.isPermanentlyDenied) {
openAppSettings();
}
}開(kāi)始錄音
/// 開(kāi)始錄音
_startRecorder() async {
try {
//獲取麥克風(fēng)權(quán)限
await getPermissionStatus().then((value) async {
if (!value) {
return;
}
//用戶允許使用麥克風(fēng)之后開(kāi)始錄音
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,
);
/// 監(jiān)聽(tīng)錄音
_recorderSubscription = recorderModule.onProgress!.listen((e) {
var date = DateTime.fromMillisecondsSinceEpoch(
e.duration.inMilliseconds,
isUtc: true);
var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
//設(shè)置了最大錄音時(shí)長(zhǎng)
if (date.second >= _maxLength) {
_stopRecorder();
return;
}
setState(() {
//更新錄音時(shí)長(zhǎng)
_recordText = txt.substring(1, 5);
});
});
setState(() {
//更新錄音狀態(tài)和錄音文件路徑
_state = RecordPlayState.recording;
_path = path;
});
});
} catch (err) {
setState(() {
_stopRecorder();
_state = RecordPlayState.record;
_cancelRecorderSubscriptions();
});
}
}結(jié)束錄音
/// 結(jié)束錄音
_stopRecorder() async {
try {
await recorderModule.stopRecorder();
_cancelRecorderSubscriptions();
// _getDuration();
} catch (err) {}
setState(() {
_state = RecordPlayState.record;
});
}
///銷(xiāo)毀錄音
void dispose() {
super.dispose();
_cancelRecorderSubscriptions();
_releaseFlauto();
}
/// 取消錄音監(jiān)聽(tīng)
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();
}播放常見(jiàn)的方法
初始化播放器
FlutterSoundPlayer playerModule = FlutterSoundPlayer();
初始化操作
init() async {
await playerModule.closePlayer();
await playerModule.openPlayer();
await playerModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//這塊是設(shè)置音頻,暫時(shí)沒(méi)用到可以不用設(shè)置
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,
));
}開(kāi)始播放
///開(kāi)始播放,這里做了一個(gè)播放狀態(tài)的回調(diào)
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 {}
}
//監(jiān)聽(tīng)播放進(jìn)度
_playerSubscription = playerModule.onProgress!.listen((e) {});
callBack(1);
} catch (err) {
callBack(0);
}
}結(jié)束播放
/// 結(jié)束播放
void stopPlayer() async {
try {
await playerModule.stopPlayer();
cancelPlayerSubscriptions();
} catch (err) {}
}
/// 取消播放監(jiān)聽(tīng)
void cancelPlayerSubscriptions() {
if (_playerSubscription != null) {
_playerSubscription!.cancel();
_playerSubscription = null;
}
}
///獲取播放狀態(tài)
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();
}動(dòng)畫(huà)實(shí)現(xiàn)
在進(jìn)行錄音和播放的過(guò)程中難免使用到動(dòng)畫(huà),這里我說(shuō)下如何加載gif和動(dòng)畫(huà)文件
加載GIF動(dòng)畫(huà)
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,
),
)加載動(dòng)畫(huà)文件
Lottie.asset('assets/-comm/record_audio_animation.json',
height: 25,
width: ScreenAdapter.screenWidth() -ScreenAdapter.width(160),
animate: true)上傳文件
上傳音頻文件
var map = {
"file": MultipartFile.fromBytes(
await File.fromUri(Uri(path: path)).readAsBytes(),
filename: "$fileName.mp3",
contentType: MediaType.parse("audio/mp3"))
};總結(jié)
上面介紹了如何錄音,如何播放本地和遠(yuǎn)程音頻文件,以及如何實(shí)現(xiàn)動(dòng)畫(huà),在錄制完音頻文件后如何上傳,這些都是我們平常使用這個(gè)功能會(huì)遇到的問(wèn)題。在使用的過(guò)程中遇到的問(wèn)題也有列出,希望對(duì)您有所幫助。
到此這篇關(guān)于基于flutter sound插件實(shí)現(xiàn)錄音與播放功能的文章就介紹到這了,更多相關(guān)flutter sound錄音與播放內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
分析Android常見(jiàn)的內(nèi)存泄露和解決方案
內(nèi)存泄漏(Memory Leak)是指程序中己動(dòng)態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無(wú)法釋放,造成系統(tǒng)內(nèi)存的浪費(fèi),導(dǎo)致程序運(yùn)行速度減慢甚至系統(tǒng)崩潰 (OOM) 等嚴(yán)重后果2021-06-06
Android 7.0系統(tǒng)webview 顯示https頁(yè)面空白處理方法
今天小編就為大家分享一篇Android 7.0系統(tǒng)webview 顯示https頁(yè)面空白處理方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-07-07
Android 后臺(tái)生成長(zhǎng)圖并分享示例(非長(zhǎng)截圖)
這篇文章主要介紹了Android 后臺(tái)生成長(zhǎng)圖并分享示例(非長(zhǎng)截圖),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
Android Kotlin環(huán)境使用ButterKnife的方法
本篇文章主要介紹了Android Kotlin環(huán)境使用ButterKnife的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
自定義Adapter并通過(guò)布局泵LayoutInflater抓取layout模板編輯每一個(gè)item實(shí)現(xiàn)思路
自定義Adapter并通過(guò)布局泵LayoutInflater抓取layout模板編輯每一個(gè)item,下面我們開(kāi)始學(xué)習(xí)這一篇的內(nèi)容,感興趣的朋友可以了解下哈2013-06-06
Android實(shí)現(xiàn)CoverFlow效果控件的實(shí)例代碼
這篇文章主要介紹了Android實(shí)現(xiàn)CoverFlow效果控件的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等)
這篇文章主要介紹了Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等),以簡(jiǎn)單實(shí)例形式分析了Android實(shí)現(xiàn)全屏、豎屏及一直顯示等的技巧與注意事項(xiàng),需要的朋友可以參考下2015-10-10
Android 自定義Switch開(kāi)關(guān)按鈕的樣式實(shí)例詳解
本文主要講的是在Android原生Switch控件的基礎(chǔ)上進(jìn)行樣式自定義,內(nèi)容很簡(jiǎn)單,但是在實(shí)現(xiàn)的過(guò)程中還是遇到了一些問(wèn)題,在此記錄下來(lái),需要的朋友參考下吧2017-12-12
Android 改變圖標(biāo)原有顏色和搜索框的實(shí)例代碼
讓Android也能有iOS那么方便的圖片色調(diào)轉(zhuǎn)換,就像同一個(gè)圖標(biāo),但是有多個(gè)地方使用,并且顏色不一樣,就可以用這個(gè)方法了。 本文實(shí)現(xiàn)TextView圖片和文字居中,鍵盤(pán)搜索功能,具體實(shí)現(xiàn)代碼大家跟隨腳本之家小編看看吧2017-09-09

