- 作者:老汪软件技巧
- 发表时间:2024-12-27 21:06
- 浏览量:
音视频播放是许多应用程序中的关键功能,能够提供丰富的多媒体体验。本文将介绍如何在 Flutter 中实现音视频播放功能,并提供具体的代码
视频
播放视频可以安装 video_player,最新版本可以在 pub.dev 中查看
dependencies:
flutter:
sdk: flutter
video_player: ^2.9.2
要求实现的具体功能是,点击视频,显示底部的工具栏,工具栏中有一个按钮在最下方,用于切换播放和暂停,上方有进度条,支持拖拽滑动,进度条左侧是当前播放的进度,右边是总时间
以下是具体代码
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoComponent extends StatefulWidget {
const VideoComponent({super.key});
@override
VideoComponentState createState() => VideoComponentState();
}
class VideoComponentState extends State<VideoComponent> {
late VideoPlayerController _controller;
bool _showControls = false;
Duration _position = Duration.zero;
@override
void initState() {
super.initState();
_controller = VideoPlayerController.networkUrl(
Uri.parse(
'https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4'),
)..initialize().then((_) {
setState(() {});
});
// 添加监听器
_controller.addListener(() {
setState(() {
_position = _controller.value.position;
});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return [
if (hours > 0) twoDigits(hours),
twoDigits(minutes),
twoDigits(seconds),
].join(':');
}
@override
Widget build(BuildContext context) {
return Center(
child: _controller.value.isInitialized
? Stack(
alignment: Alignment.bottomCenter,
children: [
GestureDetector(
onTap: () {
setState(() {
_showControls = !_showControls;
});
},
child: AspectRatio(
aspectRatio: _controller.value.aspectRatio,
child: VideoPlayer(_controller),
),
),
if (_showControls) _buildControls(),
],
)
: const CircularProgressIndicator(),
);
}
Widget _buildControls() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
_formatDuration(_position),
style: const TextStyle(color: Colors.white),
),
),
Expanded(
child: VideoProgressIndicator(
_controller,
allowScrubbing: true,
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
_formatDuration(_controller.value.duration),
style: const TextStyle(color: Colors.white),
),
),
],
),
IconButton(
onPressed: () {
setState(() {
_controller.value.isPlaying
? _controller.pause()
: _controller.play();
});
},
icon: Icon(
_controller.value.isPlaying ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 30.0,
),
),
],
);
}
}
_controller 是视频播放器的控制器,管理视频的播放、暂停等操作
_showControls 是一个布尔值,用于控制是否显示播放控件
_position 存储当前播放进度,addListener 用于监听视频播放进度的变化,并在进度更新时调用 setState,如果不监听,直接使用 _formatDuration(controller.value.position) 则无法实时获取进度
音频
播放音频采用的是 just_audio,最新版本同样在 pub.dev 中查看
要求实现的具体功能是左边是一个按钮,用于切换播放和暂停,右边有一个进度条,支持拖拽滑动,进度条左边是当前播放的进度,进度条右边是总时间
以下是具体代码
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
class SoundComponent extends StatefulWidget {
const SoundComponent({super.key});
@override
SoundComponentState createState() => SoundComponentState();
}
class SoundComponentState extends State<SoundComponent> {
late AudioPlayer _player;
Duration _duration = Duration.zero;
Duration _position = Duration.zero;
late StreamSubscription<Duration?> _durationSubscription;
late StreamSubscription<Duration> _positionSubscription;
late StreamSubscription _playerStateSubscription;
@override
void initState() {
super.initState();
_player = AudioPlayer();
_init();
_durationSubscription = _player.durationStream.listen((duration) {
setState(() {
_duration = duration ?? Duration.zero;
});
});
_positionSubscription = _player.positionStream.listen((position) {
setState(() {
_position = position;
});
});
_playerStateSubscription = _player.playerStateStream.listen((state) {
if (state.processingState == ProcessingState.ready) {
setState(() {
_duration = _player.duration ?? Duration.zero;
});
}
});
}
Future<void> _init() async {
try {
await _player.setUrl(
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3'); // 设置音频 URL
} catch (e) {
print("Error loading audio: $e");
}
}
@override
void dispose() {
_durationSubscription.cancel();
_positionSubscription.cancel();
_playerStateSubscription.cancel();
_player.dispose();
super.dispose();
}
String _formatDuration(Duration duration) {
String twoDigits(int n) => n.toString().padLeft(2, '0');
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return '${twoDigits(minutes)}:${twoDigits(seconds)}';
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
padding: const EdgeInsets.all(10),
color: Colors.blue,
child: Row(
children: [
GestureDetector(
onTap: () {
setState(() {
if (_player.playing) {
_player.pause();
} else {
_player.play();
}
});
},
child: Container(
width: 48.0,
height: 48.0,
child: Icon(
_player.playing ? Icons.pause : Icons.play_arrow,
color: Colors.white,
size: 32.0,
),
),
),
Text(_formatDuration(Duration(seconds: _position.inSeconds))),
Expanded(
child: Slider(
value: _position.inSeconds.toDouble(),
max: _duration.inSeconds.toDouble(),
onChanged: (value) {
_player.seek(Duration(seconds: value.toInt()));
},
)),
Text(_formatDuration(Duration(seconds: _duration.inSeconds))),
],
)));
}
}
在代码中,我们创建 AudioPlayer 实例并调用 _init 方法,然后订阅 durationStream 和 positionStream,监听音频时长和位置的变化,并在变化时更新状态,为了避免内存泄漏问题,在 dispose 方法中取消流的订阅并释放音频播放器的资源
_playerStateSubscription 的作用是为了不需要通过点击左侧按钮就可以获取总时长,大部分情况下如果你设置了监听 playState 并获取总时长的操作的话,实际上是不需要 _durationSubscription 的,上述代码也一并列出