Flutter自定义功能插件flutter_silk的使用
Flutter自定义功能插件flutter_silk的使用
flutter_silk
是一个用于将音频从silk格式转换为pcm/mp3格式的Flutter ffi插件。它支持MacOS、iOS、Linux、Windows和Android平台。
使用
以下是如何使用flutter_silk
的基本示例:
final silkData = File("input.silk").readAsBytesSync();
var output = silkToMp3(silkData);
File("output.mp3").writeAsBytesSync(output!);
要查看完整的示例,请访问/example
文件夹。
项目结构
此模板使用以下结构:
src
: 包含本地源代码及用于构建动态库的Cmake文件。lib
: 包含插件的Dart代码,并使用dart:ffi
调用本地代码。- 平台文件夹(如
android
、ios
、windows
等):包含用于构建和捆绑本地代码库与平台应用程序的构建文件。
绑定到本地代码
为了使用本地代码,需要在Dart中创建绑定。为了避免手动编写这些绑定,可以使用package:ffigen
从头文件src/flutter_silk.h
生成绑定。
重新生成绑定可以通过运行以下命令完成:
flutter pub run ffigen --config ffigen.yaml
调用本地代码
一些本地函数可以直接从任何隔离区调用。例如,在lib/flutter_silk.dart
中可以看到silkToPcm
、pcmToMp3
和silkToMp3
等函数。
完整示例
以下是完整的示例代码,位于example/lib/main.dart
文件中:
import 'dart:io';
import 'package:audioplayers/audioplayers.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_silk/flutter_silk.dart';
import 'package:path/path.dart' as path;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String? inputPath;
String? outputDir;
String outputName = "output.mp3";
bool working = false;
bool done = false;
final player = AudioPlayer();
void selectInputFile() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
dialogTitle: "Select Silk file",
allowedExtensions: ["silk"],
allowMultiple: false);
if (result == null) {
return;
}
if (result.paths.isEmpty) {
return;
}
setState(() {
inputPath = result.paths[0];
});
}
void encodeMp3() async {
if (inputPath == null) {
throw StateError("inputPath should not be null");
}
outputDir = await FilePicker.platform.getDirectoryPath(
dialogTitle: "Pick a directory to save output MP3 file");
if (outputDir == null) {
return;
}
setState(() {
working = true;
});
try {
print("input file: ${inputPath}");
final silkData = File(inputPath!).readAsBytesSync();
var output = silkToMp3(silkData);
final File f = File(path.join(outputDir!, outputName));
if (output != null) {
f.writeAsBytesSync(output!);
print("Output file: ${f.path}");
}
print("Done ${silkData.length}, output -> ${output == null}");
setState(() {
done = true;
});
} catch (e) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text("Error"),
content: Text(e.toString()),
));
} finally {
setState(() {
working = false;
});
}
}
[@override](/user/override)
Widget build(BuildContext context) {
const textStyle = TextStyle(fontSize: 25);
const spacerSmall = SizedBox(height: 10);
const spacerLarge = SizedBox(height: 30);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter SILK & LAME Example'),
),
body: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(10),
child: Column(
children: [
const Text(
'Call SILK/LAME API through FFI that is shipped as source in the package. '
'The native code is built as part of the Flutter Runner build.',
style: textStyle,
textAlign: TextAlign.center,
),
const Divider(),
spacerLarge,
ElevatedButton(
onPressed: !working ? selectInputFile : null,
child: const Text(
"Select Silk file",
style: textStyle,
)),
spacerSmall,
RichText(
text: TextSpan(
style: const TextStyle(fontSize: 25, color: Colors.black),
children: [
const TextSpan(
text: "Input Silk file: ",
style: TextStyle(fontWeight: FontWeight.bold)),
TextSpan(text: inputPath)
]),
textAlign: TextAlign.center,
),
spacerSmall,
TextFormField(
onChanged: (v) => setState(() {
outputName = v;
}),
decoration:
const InputDecoration(labelText: "Output MP3 filename"),
initialValue: outputName),
spacerSmall,
ElevatedButton(
onPressed:
inputPath != null && outputName.isNotEmpty && !working
? encodeMp3
: null,
child: const Text(
"Encode to MP3",
style: textStyle,
)),
spacerSmall,
ElevatedButton(
child: const Text(
'Play',
style: textStyle,
),
onPressed: () async {
done
? await player.play(
DeviceFileSource(path.join(outputDir!, outputName)))
: null;
},
),
spacerSmall,
if (working) const CircularProgressIndicator(),
],
),
),
),
),
);
}
}
更多关于Flutter自定义功能插件flutter_silk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义功能插件flutter_silk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,flutter_silk
是一个假设的 Flutter 插件名称,为了演示如何自定义和使用 Flutter 插件,我将基于一般 Flutter 插件开发的步骤和假设的 flutter_silk
插件功能来编写一个代码案例。请注意,实际插件的名称和功能可能与假设的不同,这里主要是演示流程。
1. 创建 Flutter 插件项目
首先,你需要使用 Flutter 命令行工具来创建一个新的 Flutter 插件项目。
flutter create --org com.example --template=plugin flutter_silk
2. 配置插件的 pubspec.yaml
在插件项目的根目录下,找到并编辑 pubspec.yaml
文件,添加插件的基本信息和依赖。
name: flutter_silk
description: A new Flutter plugin.
version: 0.1.0
homepage: https://github.com/example/flutter_silk
environment:
sdk: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
flutter:
plugin:
platforms:
android:
package: com.example.flutter_silk
pluginClass: FlutterSilkPlugin
ios:
pluginClass: FlutterSilkPlugin
3. 实现平台特定代码
Android 平台
在 android/src/main/java/com/example/flutter_silk/FlutterSilkPlugin.java
中实现插件的功能。
package com.example.flutter_silk;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.embedding.android.FlutterActivity;
import android.app.Activity;
import android.content.Context;
public class FlutterSilkPlugin implements FlutterPlugin, ActivityAware {
private MethodChannel channel;
private Activity activity;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_silk");
channel.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getPlatformVersion")) {
String version = android.os.Build.VERSION.RELEASE;
result.success(version);
} else {
result.notImplemented();
}
}
);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
}
@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
activity = binding.getActivity();
}
@Override
public void onDetachedFromActivityForConfigChanges() {
activity = null;
}
@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
activity = binding.getActivity();
}
@Override
public void onDetachedFromActivity() {
activity = null;
}
}
iOS 平台
在 ios/Classes/FlutterSilkPlugin.swift
中实现插件的功能。
import Flutter
public class FlutterSilkPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_silk", binaryMessenger: registrar.messenger())
let instance = FlutterSilkPlugin()
_ = channel.setMethodCallHandler(instance.handle(_:result:))
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getPlatformVersion" {
let version = UIDevice.current.systemVersion
result(version)
} else {
result(FlutterMethodNotImplemented)
}
}
}
4. 在 Flutter 项目中使用插件
现在,你已经创建了一个基本的 Flutter 插件。接下来,在你的 Flutter 应用项目中使用这个插件。
添加插件依赖
在你的 Flutter 应用项目的 pubspec.yaml
文件中添加对 flutter_silk
插件的依赖。
dependencies:
flutter:
sdk: flutter
flutter_silk:
path: ../path/to/flutter_silk # 指向你的插件项目路径
使用插件
在你的 Flutter 应用项目的 Dart 代码中导入并使用插件。
import 'package:flutter/material.dart';
import 'package:flutter_silk/flutter_silk.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
String _platformVersion = 'Unknown platform version';
@override
void initState() {
super.initState();
_getPlatformVersion();
}
Future<void> _getPlatformVersion() async {
String version;
try {
version = await FlutterSilk.platformVersion;
} on PlatformException {
version = 'Failed to get platform version.';
}
if (!mounted) return;
setState(() {
_platformVersion = version;
});
}
@override
Widget build(BuildContext context) {
return Text('Running on: $_platformVersion\n');
}
}
在这个示例中,FlutterSilk.platformVersion
调用插件的 getPlatformVersion
方法。注意,这里的 FlutterSilk
类和 platformVersion
方法是假设的,实际使用时你需要根据插件的 API 文档来调用正确的方法和属性。
请注意,这只是一个基本的插件开发示例,实际开发中可能涉及更多复杂的逻辑和平台特定的实现。希望这个示例能帮助你理解如何创建和使用 Flutter 自定义插件。