当前位置:网站首页>Develop plug-ins for the recording function of flutter
Develop plug-ins for the recording function of flutter
2022-07-28 01:31:00 【dengjiangszhan】
Native provides functionality ,Dart module adopt method channel Asynchronous call
1, Android part
Manual registration
Flutter Official practice , Is to automatically register plug-ins ,
Very convenient
Manual registration , Reflect the difference of this article
The plug-in is AudioRecorderPlugin
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
flutterEngine!!.plugins.add(AudioRecorderPlugin())
}
}
Android and Dart Communication of
Mainly message callback
The following are ,
- Start the recording
- End of the tape
- Is the tape
- Whether there is recording permission
Be careful , The recording permission here includes two , Microphone permissions , And storage permissions
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
switch (call.method) {
case "start":
Log.d(LOG_TAG, "Start");
Log.d(LOG_TAG, "11111____");
String path = call.argument("path");
mExtension = call.argument("extension");
startTime = Calendar.getInstance().getTime();
if (path != null) {
mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + path;
} else {
Log.d(LOG_TAG, "11111____222");
String fileName = String.valueOf(startTime.getTime());
mFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + fileName + mExtension;
}
Log.d(LOG_TAG, mFilePath);
startRecording();
isRecording = true;
result.success(null);
break;
case "stop":
Log.d(LOG_TAG, "Stop");
stopRecording();
long duration = Calendar.getInstance().getTime().getTime() - startTime.getTime();
Log.d(LOG_TAG, "Duration : " + String.valueOf(duration));
isRecording = false;
HashMap<String, Object> recordingResult = new HashMap<>();
recordingResult.put("duration", duration);
recordingResult.put("path", mFilePath);
recordingResult.put("audioOutputFormat", mExtension);
result.success(recordingResult);
break;
case "isRecording":
Log.d(LOG_TAG, "Get isRecording");
result.success(isRecording);
break;
case "hasPermissions":
Log.d(LOG_TAG, "Get hasPermissions");
Context context = _flutterBinding.getApplicationContext();
PackageManager pm = context.getPackageManager();
int hasStoragePerm = pm.checkPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context.getPackageName());
int hasRecordPerm = pm.checkPermission(Manifest.permission.RECORD_AUDIO, context.getPackageName());
boolean hasPermissions = hasStoragePerm == PackageManager.PERMISSION_GRANTED && hasRecordPerm == PackageManager.PERMISSION_GRANTED;
result.success(hasPermissions);
break;
default:
result.notImplemented();
break;
}
}
Android recording
Use wav The packaging format of , use AudioRecord;
Other packaging formats , use MediaRecorder
The upper two players , It has the function of starting and ending recording ;
Pause recording and resume recording , Then start and end many times , Then put the documents together

2,Dart module part
establish MethodChannel, Call the above native functions asynchronously
class AudioRecorder {
static const MethodChannel _channel = const MethodChannel('audio_recorder');
static LocalFileSystem fs = LocalFileSystem();
static Future start(String path, AudioOutputFormat audioOutputFormat) async {
String extension;
if (path != null) {
if (audioOutputFormat != null) {
if (_convertStringInAudioOutputFormat(p.extension(path)) !=
audioOutputFormat) {
extension = _convertAudioOutputFormatInString(audioOutputFormat);
path += extension;
} else {
extension = p.extension(path);
}
} else {
if (_isAudioOutputFormat(p.extension(path))) {
extension = p.extension(path);
} else {
extension = ".m4a"; // default value
path += extension;
}
}
File file = fs.file(path);
if (await file.exists()) {
throw new Exception("A file already exists at the path :" + path);
} else if (!await file.parent.exists()) {
throw new Exception("The specified parent directory does not exist");
}
} else {
extension = ".m4a"; // default value
}
return _channel
.invokeMethod('start', {"path": path, "extension": extension});
}
static Future<Recording?> stop() async {
// Bring out the original information , Put it in the dictionary
Map<String, dynamic> response =
Map.from(await _channel.invokeMethod('stop'));
if (response != null) {
int duration = response['duration'];
String fmt = response['audioOutputFormat'];
AudioOutputFormat? outputFmt = _convertStringInAudioOutputFormat(fmt);
if (fmt != null && outputFmt != null) {
Recording recording = new Recording(
new Duration(milliseconds: duration),
response['path'],
outputFmt,
response['audioOutputFormat']);
return recording;
}
} else {
return null;
}
}
iOS part
Manually register the plug-in
The plug-in name here , by SwiftAudioRecorderPlugin
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if let register = registrar(forPlugin: "SwiftAudioRecorderPlugin"){
SwiftAudioRecorderPlugin.register(with: register)
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
iOS plug-in unit
Logic is similar to Android plug-ins ,
because iOS Of AVAudioRecorder Yes pause and resume operation , Support friendship ,
So the function of pause and resume recording is added
iOS The permission of the client is greater than that of Android , One less
Only recording microphone permission is required
public class SwiftAudioRecorderPlugin: NSObject, FlutterPlugin {
var isRecording = false
var hasPermissions = false
var mExtension = ""
var mPath = ""
var startTime: Date!
var audioRecorder: AVAudioRecorder?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "audio_recorder", binaryMessenger: registrar.messenger())
let instance = SwiftAudioRecorderPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "start":
print("start")
let dic = call.arguments as! [String : Any]
mExtension = dic["extension"] as? String ?? ""
mPath = dic["path"] as? String ?? ""
startTime = Date()
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
if mPath == "" {
mPath = documentsPath + "/" + String(Int(startTime.timeIntervalSince1970)) + ".m4a"
}
else{
mPath = documentsPath + "/" + mPath
}
print("path: " + mPath)
let settings = [
AVFormatIDKey: getOutputFormatFromString(mExtension),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord, options: AVAudioSession.CategoryOptions.defaultToSpeaker)
try AVAudioSession.sharedInstance().setActive(true)
let recorder = try AVAudioRecorder(url: URL(string: mPath)!, settings: settings)
recorder.delegate = self
recorder.record()
audioRecorder = recorder
} catch {
print("fail")
result(FlutterError(code: "", message: "Failed to record", details: nil))
}
isRecording = true
result(nil)
case "pause":
audioRecorder?.pause()
result(nil)
case "resume":
audioRecorder?.record()
result(nil)
case "stop":
print("stop")
audioRecorder?.stop()
audioRecorder = nil
let duration = Int(Date().timeIntervalSince(startTime as Date) * 1000)
isRecording = false
var recordingResult = [String : Any]()
recordingResult["duration"] = duration
recordingResult["path"] = mPath
recordingResult["audioOutputFormat"] = mExtension
result(recordingResult)
case "isRecording":
print("isRecording")
result(isRecording)
case "hasPermissions":
print("hasPermissions")
switch AVAudioSession.sharedInstance().recordPermission{
case AVAudioSession.RecordPermission.granted:
print("granted")
hasPermissions = true
case AVAudioSession.RecordPermission.denied:
print("denied")
hasPermissions = false
case AVAudioSession.RecordPermission.undetermined:
print("undetermined")
AVAudioSession.sharedInstance().requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
self.hasPermissions = true
} else {
self.hasPermissions = false
}
}
}
default:()
}
result(hasPermissions)
default:
result(FlutterMethodNotImplemented)
}
}
}
Dart Call part
By judging the platform ,Platform.isIOS,
to iOS equipment , Add perfect functions
@override
Widget build(BuildContext context) {
final VoidCallback tapFirst;
if (Platform.isAndroid && name == kEnd) {
tapFirst = _audioEnd;
} else {
tapFirst = _audioGoOn;
}
List<Widget> views = [
ElevatedButton(
child: Text(
name,
style: Theme.of(context).textTheme.headline4,
),
onPressed: tapFirst,
)
];
if (Platform.isIOS && name != kStarted) {
views.add(SizedBox(height: 80));
views.add(ElevatedButton(
child: Text(
kEnd,
style: Theme.of(context).textTheme.headline4,
),
onPressed: _audioEnd,
));
}
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: views,
),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
github repo
边栏推荐
- Oracle grouping takes the maximum value
- How the test architects of bat factories interpret various disputes of the test platform
- Cesium add annular diffusion ripple
- Realize ABCD letter increment
- Spool timer
- BSP视频教程第21期:轻松一键实现串口DMA不定长收发,支持裸机和RTOS,含MDK和IAR两种玩法,比STM32CubeMX还方便(2022-07-24)
- Shaanxi Yuanjie semiconductor, a laser chip manufacturer, was invested by 8 investment institutions including GF Securities and CITIC Securities
- EWM receiving ECC delivery note verification logic problem
- Briefly understand namenode and datanode
- Jingfang Technology: ASML, a lithography machine manufacturer, is one of the main customers of Anterion company, which participated in the merger and acquisition of the company
猜你喜欢

2022/07/27 learning notes (Day17) code blocks and internal classes

idea常用的快捷键汇总

EWM receiving ECC delivery note verification logic problem

ICML2022 | 在线决策Transformer

字节月薪28K,分享一波我的自动化测试经验....

【游戏】任天堂Nintendo Switch超详细购买/使用指南以及注意事项(根据自己使用持续更新中...)

BSP视频教程第21期:轻松一键实现串口DMA不定长收发,支持裸机和RTOS,含MDK和IAR两种玩法,比STM32CubeMX还方便(2022-07-24)

Realize ABCD letter increment

Go language variable

【C语言】文件操作
随机推荐
Redis cache penetration breakdown and avalanche
Shutter -- password login registration interface
Summary of common shortcut keys in idea
Unknown database ‘xxxxx‘
Advanced MySQL -- stored procedures and custom functions
JUC concurrent programming learning
How to make digital retail undertake the development task of the era of traffic and retention may be the key
The cooperation between starfish OS and metabell is just the beginning
吴雄昂发内部信:Arm的指控是莫须有的,现有成果不允许有任何损害!
Rviz 使用Arbotix控制机器人运动
Realize ABCD letter increment
S-RPN: Sampling-balanced region proposal network for small crop pest detection
Rviz uses arbotix to control robot motion
ABAP CDs table function introduction and examples
力挺吴雄昂!Arm中国管理层发公开信:对莫须有的指控感到震惊和愤怒!
Flutter 通话界面UI
How the test architects of bat factories interpret various disputes of the test platform
Day 013 一维数组练习
Byte monthly salary 28K, share a wave of my automation testing experience
spreadsheet 导出 excel表格