基于flutter?sound插件實現(xiàn)錄音與播放功能
插件介紹:
flutter_sound這個插件可以實現(xiàn)iOS和Android平臺的錄音和播放功能。即可以播放本地音頻文件,也可以播放遠(yuǎn)程URL文件。在這里我講介紹這個插件的用法以及碰到的一些常見問題如何解決。
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)用來干嘛</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庫,具體路徑

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

這里還用了下面幾個插件
權(quán)限管理插件 permission_handler
插件信息:permission_handler: ^9.2.0
插件地址:pub.flutter-io.cn/packages/pe…
音頻硬件配置插件 audio_session
插件信息:audio_session: ^0.1.6
動畫插件
插件信息:lottie: 1.2.1
插件地址:pub.flutter-io.cn/packages/fl…
常用的方法
錄音常見的方法
初始化錄音對象
FlutterSoundRecorder recorderModule = FlutterSoundRecorder();
開啟錄音
Future<void> init() async {
//開啟錄音
await recorderModule.openRecorder();
//設(shè)置訂閱計時器
await recorderModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//初始化日期插件
await initializeDateFormatting();
}麥克風(fēng)權(quán)限
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;
}
///申請權(quán)限
void requestPermission(Permission permission) async {
PermissionStatus status = await permission.request();
if (status.isPermanentlyDenied) {
openAppSettings();
}
}開始錄音
/// 開始錄音
_startRecorder() async {
try {
//獲取麥克風(fēng)權(quán)限
await getPermissionStatus().then((value) async {
if (!value) {
return;
}
//用戶允許使用麥克風(fēng)之后開始錄音
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)聽錄音
_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è)置了最大錄音時長
if (date.second >= _maxLength) {
_stopRecorder();
return;
}
setState(() {
//更新錄音時長
_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;
});
}
///銷毀錄音
void dispose() {
super.dispose();
_cancelRecorderSubscriptions();
_releaseFlauto();
}
/// 取消錄音監(jiān)聽
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();
}播放常見的方法
初始化播放器
FlutterSoundPlayer playerModule = FlutterSoundPlayer();
初始化操作
init() async {
await playerModule.closePlayer();
await playerModule.openPlayer();
await playerModule
.setSubscriptionDuration(const Duration(milliseconds: 10));
//這塊是設(shè)置音頻,暫時沒用到可以不用設(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,
));
}開始播放
///開始播放,這里做了一個播放狀態(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)聽播放進度
_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)聽
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();
}動畫實現(xiàn)
在進行錄音和播放的過程中難免使用到動畫,這里我說下如何加載gif和動畫文件
加載GIF動畫
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,
),
)加載動畫文件
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)程音頻文件,以及如何實現(xiàn)動畫,在錄制完音頻文件后如何上傳,這些都是我們平常使用這個功能會遇到的問題。在使用的過程中遇到的問題也有列出,希望對您有所幫助。
到此這篇關(guān)于基于flutter sound插件實現(xiàn)錄音與播放功能的文章就介紹到這了,更多相關(guān)flutter sound錄音與播放內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 7.0系統(tǒng)webview 顯示https頁面空白處理方法
今天小編就為大家分享一篇Android 7.0系統(tǒng)webview 顯示https頁面空白處理方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-07-07
Android Kotlin環(huán)境使用ButterKnife的方法
本篇文章主要介紹了Android Kotlin環(huán)境使用ButterKnife的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03
自定義Adapter并通過布局泵LayoutInflater抓取layout模板編輯每一個item實現(xiàn)思路
自定義Adapter并通過布局泵LayoutInflater抓取layout模板編輯每一個item,下面我們開始學(xué)習(xí)這一篇的內(nèi)容,感興趣的朋友可以了解下哈2013-06-06
Android實現(xiàn)CoverFlow效果控件的實例代碼
這篇文章主要介紹了Android實現(xiàn)CoverFlow效果控件的實例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等)
這篇文章主要介紹了Android中Activity常用功能設(shè)置小結(jié)(包括全屏、橫豎屏等),以簡單實例形式分析了Android實現(xiàn)全屏、豎屏及一直顯示等的技巧與注意事項,需要的朋友可以參考下2015-10-10
Android 自定義Switch開關(guān)按鈕的樣式實例詳解
本文主要講的是在Android原生Switch控件的基礎(chǔ)上進行樣式自定義,內(nèi)容很簡單,但是在實現(xiàn)的過程中還是遇到了一些問題,在此記錄下來,需要的朋友參考下吧2017-12-12
Android 改變圖標(biāo)原有顏色和搜索框的實例代碼
讓Android也能有iOS那么方便的圖片色調(diào)轉(zhuǎn)換,就像同一個圖標(biāo),但是有多個地方使用,并且顏色不一樣,就可以用這個方法了。 本文實現(xiàn)TextView圖片和文字居中,鍵盤搜索功能,具體實現(xiàn)代碼大家跟隨腳本之家小編看看吧2017-09-09

