Flutter音频裁剪插件easy_audio_trimmer的使用

Flutter音频裁剪插件easy_audio_trimmer的使用

特性

  • 在Flutter应用中修剪音频文件。
  • 指定修剪音频文件的起始时间和结束时间。
  • 音频播放控制。
  • 获取并存储音频文件。
  • 修剪后的音频文件保存在设备上。
  • 使用简单,只需调用一个函数。
  • 兼容大多数音频文件格式(如mp3, wav, aac等)。
  • 支持iOS和Android平台。

示例

以下是easy_audio_trimmer在Android设备上运行的演示:

音频修剪器演示

使用方法

添加依赖

在你的pubspec.yaml文件中添加easy_audio_trimmer依赖:

dependencies:
  easy_audio_trimmer: ^1.0.0

Android配置

无需额外配置即可在Android平台上使用。

iOS配置

  • 设置ios/Podfile的平台版本为10
platform :ios, '10'

功能实现

加载输入音频文件

final Trimmer _trimmer = Trimmer();
await _trimmer.loadAudio(audioFile: widget.file);

保存修剪后的音频

_trimmer.saveTrimmedAudio(
  startValue: _startValue,
  endValue: _endValue,
  audioFileName: DateTime.now().millisecondsSinceEpoch.toString(),
  onSave: (outputPath) {
    setState(() {
      _progressVisibility = false;
    });
    debugPrint('OUTPUT PATH: $outputPath');
  },
);

音频播放状态

await _trimmer.audioPlaybackControl(
  startValue: _startValue,
  endValue: _endValue,
);

小部件

显示音频修剪区域

TrimViewer(
  trimmer: _trimmer,
  viewerHeight: 50.0,
  viewerWidth: MediaQuery.of(context).size.width,
  maxAudioLength: const Duration(seconds: 10),
  onChangeStart: (value) => _startValue = value,
  onChangeEnd: (value) => _endValue = value,
  allowAudioSelection: true,
  onChangePlaybackState: (value) =>
      setState(() => _isPlaying = value),
)

示例代码

您可以尝试通过替换新创建的Flutter项目的main.dart文件来使用此示例。

import 'dart:io';
import 'package:example/trimmer_view.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: createMaterialColor(const Color(0xFF332FD0)),
        ),
        home: const FileSelectorWidget());
  }
}

class FileSelectorWidget extends StatefulWidget {
  const FileSelectorWidget({super.key});

  @override
  State<FileSelectorWidget> createState() => _FileSelectorWidgetState();
}

class _FileSelectorWidgetState extends State<FileSelectorWidget> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Audio Trimmer"),
      ),
      body: Center(
        child: ElevatedButton(
            onPressed: () async {
              FilePickerResult? result = await FilePicker.platform.pickFiles(
                type: FileType.audio,
                allowCompression: false,
              );
              if (result != null) {
                File file = File(result.files.single.path!);
                // ignore: use_build_context_synchronously
                Navigator.of(context).push(
                  MaterialPageRoute(builder: (context) {
                    return AudioTrimmerView(file);
                  }),
                );
              }
            },
            child: const Text("Select File")),
      ),
    );
  }
}

class AudioTrimmerView extends StatefulWidget {
  final File file;

  const AudioTrimmerView(this.file, {Key? key}) : super(key: key);

  @override
  State<AudioTrimmerView> createState() => _AudioTrimmerViewState();
}

class _AudioTrimmerViewState extends State<AudioTrimmerView> {
  final Trimmer _trimmer = Trimmer();

  double _startValue = 0.0;
  double _endValue = 0.0;

  bool _isPlaying = false;
  bool _progressVisibility = false;
  bool isLoading = false;

  @override
  void initState() {
    super.initState();

    _loadAudio();
  }

  void _loadAudio() async {
    setState(() {
      isLoading = true;
    });
    await _trimmer.loadAudio(audioFile: widget.file);
    setState(() {
      isLoading = false;
    });
  }

  _saveAudio() {
    setState(() {
      _progressVisibility = true;
    });

    _trimmer.saveTrimmedAudio(
      startValue: _startValue,
      endValue: _endValue,
      audioFileName: DateTime.now().millisecondsSinceEpoch.toString(),
      onSave: (outputPath) {
        setState(() {
          _progressVisibility = false;
        });
        debugPrint('OUTPUT PATH: $outputPath');
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async {
        if (Navigator.of(context).userGestureInProgress) {
          return false;
        } else {
          return true;
        }
      },
      child: Scaffold(
        backgroundColor: Colors.white,
        appBar: AppBar(
          title: const Text("Audio Trimmer"),
        ),
        body: isLoading
            ? const CircularProgressIndicator()
            : Center(
                child: Container(
                  padding: const EdgeInsets.only(bottom: 30.0),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    mainAxisSize: MainAxisSize.max,
                    children: <Widget>[
                      Visibility(
                        visible: _progressVisibility,
                        child: LinearProgressIndicator(
                          backgroundColor:
                              Theme.of(context).primaryColor.withOpacity(0.5),
                        ),
                      ),
                      ElevatedButton(
                        onPressed:
                            _progressVisibility ? null : () => _saveAudio(),
                        child: const Text("SAVE"),
                      ),
                      AudioViewer(trimmer: _trimmer),
                      Center(
                        child: Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: TrimViewer(
                            trimmer: _trimmer,
                            viewerHeight: 100,
                            viewerWidth: MediaQuery.of(context).size.width,
                            durationStyle: DurationStyle.FORMAT_MM_SS,
                            backgroundColor: Theme.of(context).primaryColor,
                            barColor: Colors.white,
                            durationTextStyle: TextStyle(
                                color: Theme.of(context).primaryColor),
                            allowAudioSelection: true,
                            editorProperties: TrimEditorProperties(
                              circleSize: 10,
                              borderPaintColor: Colors.pink,
                              borderWidth: 4,
                              borderRadius: 5,
                              circlePaintColor: Colors.pink.shade800,
                            ),
                            areaProperties:
                                TrimAreaProperties.edgeBlur(blurEdges: true),
                            onChangeStart: (value) => _startValue = value,
                            onChangeEnd: (value) => _endValue = value,
                            onChangePlaybackState: (value) {
                              if (mounted) {
                                setState(() => _isPlaying = value);
                              }
                            },
                          ),
                        ),
                      ),
                      TextButton(
                        child: _isPlaying
                            ? Icon(
                                Icons.pause,
                                size: 80.0,
                                color: Theme.of(context).primaryColor,
                              )
                            : Icon(
                                Icons.play_arrow,
                                size: 80.0,
                                color: Theme.of(context).primaryColor,
                              ),
                        onPressed: () async {
                          bool playbackState =
                              await _trimmer.audioPlaybackControl(
                            startValue: _startValue,
                            endValue: _endValue,
                          );
                          setState(() => _isPlaying = playbackState);
                        },
                      )
                    ],
                  ),
                ),
              ),
      ),
    );
  }
}

排查问题

如果在Android平台上遇到minSdkVersion需要设置为24的错误,或者在iOS平台上遇到Podfile平台版本应设置为11的错误,首先检查pubspec.lock文件中的ffmpeg_kit_flutter版本是否有-LTS后缀。这应该可以解决iOS平台上的所有问题。

如果在Android上仍然遇到相同的问题,可以在<project_directory>/android/app/src/main/AndroidManifest.xml中添加以下内容:

<manifest xmlns:tools="http://schemas.android.com/tools" ....... >
    <uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter, com.arthenica.ffmpegkit" />
</manifest>

更多关于Flutter音频裁剪插件easy_audio_trimmer的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter音频裁剪插件easy_audio_trimmer的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


easy_audio_trimmer 是一个用于在 Flutter 应用中裁剪音频的插件。它提供了一个简单的界面,允许用户选择音频文件并对其进行裁剪。以下是使用 easy_audio_trimmer 的基本步骤:

1. 添加依赖

首先,在你的 pubspec.yaml 文件中添加 easy_audio_trimmer 依赖:

dependencies:
  flutter:
    sdk: flutter
  easy_audio_trimmer: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入包

在你的 Dart 文件中导入 easy_audio_trimmer 包:

import 'package:easy_audio_trimmer/easy_audio_trimmer.dart';

3. 使用 Trimmer

你可以使用 Trimmer 类来加载音频文件并进行裁剪。以下是一个简单的示例:

class AudioTrimmerExample extends StatefulWidget {
  [@override](/user/override)
  _AudioTrimmerExampleState createState() => _AudioTrimmerExampleState();
}

class _AudioTrimmerExampleState extends State<AudioTrimmerExample> {
  Trimmer _trimmer = Trimmer();

  [@override](/user/override)
  void dispose() {
    _trimmer.dispose();
    super.dispose();
  }

  Future<void> _loadAudio() async {
    // 选择音频文件
    File file = await FilePicker.getFile(type: FileType.audio);
    if (file != null) {
      await _trimmer.loadAudio(audioFile: file);
    }
  }

  Future<void> _saveAudio() async {
    // 保存裁剪后的音频
    String outputPath = await _trimmer.saveTrimmedAudio(
      startValue: _trimmer.startValue,
      endValue: _trimmer.endValue,
    );
    print('Trimmed audio saved to: $outputPath');
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Audio Trimmer'),
      ),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: _loadAudio,
            child: Text('Load Audio'),
          ),
          TrimmerView(_trimmer),
          ElevatedButton(
            onPressed: _saveAudio,
            child: Text('Save Trimmed Audio'),
          ),
        ],
      ),
    );
  }
}
回到顶部