Flutter音频编辑插件sound_edit的使用
Flutter音频编辑插件sound_edit的使用
功能介绍
此功能允许您不受文件扩展限制地合并音频文件,并且可以编辑播放时间。
开始使用
运行命令以启动应用
运行以下命令以输出在assets
目录中注册的音频文件到屏幕。
./build.sh && cd example && flutter run
如果你想修剪或录制声音,请决定文件名
示例代码
示例代码位置
示例代码位于 example/lib/main.dart
示例代码
import 'dart:async' show Future, StreamController;
import 'package:flutter/material.dart';
import "package:sound_edit/sound_edit_animation_view.dart";
import 'package:sound_edit/sound_edit_channel.dart';
import 'package:sound_edit/sound_edit_dialog.dart';
import 'package:sound_edit/sound_edit_gifImage.dart';
import 'package:sound_edit/sound_edit_override_widget.dart';
import 'package:sound_edit/sound_edit_slider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// 这个小部件是你的应用的根。
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: const MyHomePage(),
theme: ThemeData(
splashColor: Colors.transparent,
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
[@override](/user/override)
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with ChangeNotifier, TickerProviderStateMixin, WidgetsBindingObserver {
var _time = 0.0;
var _max = 1.0;
var _currentRangeValues = const RangeValues(
0.0,
1.0,
);
var _soundName = '';
var _rangeController = StreamController.broadcast();
final _dialog = SoundEditDialog();
final SoundEditChanel _methodChanel = SoundEditChanel();
final SoundEditOverrideWidget _slideUpController = SoundEditOverrideWidget();
[@override](/user/override)
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addObserver(this);
Future(() async {
_methodChanel.getAppDocumentDirectoryContent();
_methodChanel.fileList = await _methodChanel.getAudioFiles();
});
Future.delayed(const Duration(milliseconds: 200), () {
_notify();
});
}
[@override](/user/override)
void dispose() {
_rangeController.close();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
[@override](/user/override)
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
_playSound(['audioStop', 'music']);
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
centerTitle: true,
title: GestureDetector(
onTap: () => _showActionSheet(context),
child: _appBar(),
),
backgroundColor: Colors.black,
),
body: Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Image.asset(
'assets/backImage.png',
fit: BoxFit.contain,
),
),
_listFile(),
if (_methodChanel.animationFlg) ...[
SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: SoundEditAnimationView(
frameCount: 149, c: SoundEditGifController(vsync: this)),
),
],
],
),
);
}
_listFile() {
return GridView.builder(
itemCount: _methodChanel.fileList.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () async {
if (!_methodChanel.animationFlg) {
_methodChanel.animationFlg = true;
_notify();
await _methodChanel
.playSoundChoice(
'play/${_methodChanel.fileList[index].path.split("/").last}',
'music')
.whenComplete(
() async =>
{_methodChanel.animationFlg = false, _notify()},
);
}
},
title: Center(
child: _dragItem(index),
));
},
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.5,
),
);
}
/// 拖拽 StreamBuilder
_dragTarget() {
return StreamBuilder(
stream: _rangeController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return DragTarget<String>(
builder: (context, candidateData, rejectedData) {
return Material(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.only(
top: 24,
),
child: Text(
_methodChanel.dragdrop.isEmpty
? 'Drop here'
: _methodChanel.dragdrop.join(','),
style: const TextStyle(fontSize: 28, color: Colors.green),
),
));
}, onAccept: (data) {
_methodChanel
..dragdrop.add(data)
..playSoundChoice(
'drag/${_methodChanel.dragdrop.join(',')},$_soundName.wav',
'drag')
.then(
(value) {
if (value == 1.0) {
_dialog.errorSameAlertDialog(context);
_reload();
} else {
_reloadDragdrop(value);
}
},
);
});
});
}
_dragItem(int index) {
var t = Text(
_methodChanel.fileList[index].path.split("/").last,
style: const TextStyle(
color: Colors.green,
),
);
return Draggable<String>(
data: _methodChanel.fileList[index].path.split("/").last,
feedback: Material(
child: ColoredBox(
color: Colors.black,
child: t,
),
),
childWhenDragging: t,
child: t,
);
}
_widget(Map<String, String> listMap) {
final chanelPath = listMap['chanelPath'] ?? '';
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton(
onPressed: () async {
switch (chanelPath) {
case 'changeName':
_dialog.nameSelectAlertDialog(
context, (p0) => {_soundName = p0, _notify()});
break;
case 'trim':
_judgeName(
() => _methodChanel
.playSoundChoice(
'trim/${_methodChanel.dragdrop.join(',')}, ${(_currentRangeValues.start).toDouble()}, ${(_currentRangeValues.end).toDouble()}, $_soundName.wav',
'trim',
)
.then(
(value) {
if (value == 1.0) {
_dialog.errorSameAlertDialog(context);
_reload();
} else {
_reload(flg: true);
_reloadDragdrop(value);
}
},
),
);
break;
case 'audioPause':
await _playSound(['audioPause', 'music']);
break;
case 'audioStop':
await _playSound(['audioStop', 'music']);
break;
case 'record':
await _judgeName(
() async => await _playSound([
'$_soundName.wav',
'record'
]),
);
break;
case 'recordStop':
await _playSound(['recordStop', 'record']);
break;
}
},
child: Text(
chanelPath,
style: const TextStyle(color: Colors.green),
),
),
],
);
}
_appBar() {
return Container(
width: 40,
height: 40,
decoration: const BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.contain,
image: AssetImage('assets/icon.png'),
),
),
);
}
_sliderBar() {
return StreamBuilder(
stream: _rangeController.stream,
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Material(
color: Colors.transparent,
child: Column(
children: [
SoundEditSlider(
start: _currentRangeValues.start,
end: _currentRangeValues.end,
min: 0,
max: _max,
color: Colors.green,
callback: (p0) {
_currentRangeValues = RangeValues(
p0.start,
p0.end,
);
},
),
Container(
color: Colors.transparent,
padding: const EdgeInsets.only(
top: 24,
),
child: Text('playTime:$_time'),
)
],
),
);
});
}
_playSound(List<String> list) {
if (list.last == 'music') {
_methodChanel.animationFlg = false;
_notify();
}
_methodChanel
.playSoundChoice(list.first, list.last)
.whenComplete(() => _reload());
}
/// 初始化重新加载
Future<void> _reload({bool? flg}) async {
_methodChanel.fileList = await _methodChanel.getAudioFiles();
_methodChanel.dragdrop.clear();
_soundName = '';
_time = 0.0;
if (flg == null) {
_max = 1.0;
_currentRangeValues = const RangeValues(0.0, 1.0);
_notify();
}
}
/// 重新加载新数据
Future<void> _reloadDragdrop(double value) async {
_currentRangeValues = RangeValues(0.0, value * 100);
_max = value * 100;
_time = value;
_rangeController.add(_currentRangeValues);
_notify();
}
/// 声音名称检查
_judgeName(VoidCallback call) {
if (_soundName.isEmpty) {
_dialog.errorAlertDialog(context);
} else {
call();
}
}
/// setState
_notify() {
setState(
() => notifyListeners(),
);
}
_showActionSheet(BuildContext context) {
if (_rangeController.hasListener) {
_rangeController.close();
} else {
_rangeController = StreamController.broadcast();
}
_slideUpController.show(
context,
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
_dragTarget(),
_sliderBar(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_widget({'chanelPath': 'changeName'}),
_widget({'chanelPath': 'trim'}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_widget({'chanelPath': 'audioPause'}),
_widget({'chanelPath': 'audioStop'}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_widget({'chanelPath': 'record'}),
_widget({'chanelPath': 'recordStop'}),
],
),
const SizedBox(
height: 40,
)
],
),
200, // 设置速度
);
}
}
更多关于Flutter音频编辑插件sound_edit的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
1 回复
更多关于Flutter音频编辑插件sound_edit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何使用Flutter音频编辑插件sound_edit
的代码示例。这个插件提供了多种音频编辑功能,如裁剪、合并、静音等。请注意,实际使用前需要确保已在pubspec.yaml
文件中添加了该依赖并运行flutter pub get
。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加sound_edit
依赖:
dependencies:
flutter:
sdk: flutter
sound_edit: ^最新版本号 # 请替换为实际最新版本号
2. 导入插件
在你的Dart文件中导入sound_edit
插件:
import 'package:sound_edit/sound_edit.dart';
3. 使用示例
以下是一个简单的示例,展示如何使用sound_edit
插件进行音频裁剪:
import 'package:flutter/material.dart';
import 'package:sound_edit/sound_edit.dart';
import 'dart:io';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
File? _audioFile;
File? _trimmedAudioFile;
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Audio Edit Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_audioFile == null
? Text('No audio file selected.')
: Text('Audio file selected: ${_audioFile!.path}'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
// Open file picker to select an audio file
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.audio,
);
if (result != null && result.files.isNotEmpty) {
File file = File(result.files.first.path!);
setState(() {
_audioFile = file;
});
// Trim audio example: from 5 seconds to 10 seconds
double startTime = 5.0; // in seconds
double endTime = 10.0; // in seconds
_trimmedAudioFile = await SoundEdit.trim(
audioPath: _audioFile!.path,
startTime: startTime,
endTime: endTime,
);
if (_trimmedAudioFile != null) {
print('Trimmed audio saved to: ${_trimmedAudioFile!.path}');
} else {
print('Failed to trim audio.');
}
}
},
child: Text('Select Audio File'),
),
SizedBox(height: 20),
_trimmedAudioFile == null
? Container()
: ElevatedButton(
onPressed: () {
// Here you can do something with the trimmed audio file, e.g., play it or share it
print('Play or share trimmed audio: ${_trimmedAudioFile!.path}');
},
child: Text('Play/Share Trimmed Audio'),
),
],
),
),
),
);
}
}
4. 注意事项
- 确保你的应用有访问存储的权限(尤其是在Android和iOS上)。
- 插件的最新版本和API可能会有所变化,请参考官方文档以获取最新信息。
- 在实际项目中,处理文件时应考虑异常处理和用户反馈,以提高应用的健壮性和用户体验。
这个示例展示了如何使用sound_edit
插件进行音频裁剪。根据需求,你还可以探索插件提供的其他功能,如合并、静音等。