Flutter音频播放器:页面加载时无法显示音乐总时长求助
解决Flutter audioplayers播放器页面加载后直接显示总时长的问题
你当前的播放器在页面初始化后总时长显示为0:0,只有点击播放按钮后才会正常显示。核心原因是initPlayer方法中获取到音乐总时长后没有触发UI刷新,同时时长格式化逻辑也存在细节缺陷(比如秒数小于10时不会补零)。以下是具体解决方案:
1. 修复初始化时的状态更新
在initPlayer方法中获取到duration后,调用setState()让UI立刻刷新:
void initPlayer() async { await player.setSource(AssetSource("forest.mp3")); duration = await player.getDuration(); setState(() {}); // 获取总时长后立即触发UI更新 player.onPositionChanged.listen((Duration d) { setState(() { value = d.inSeconds.toDouble(); }); }); }
2. 添加时长格式化工具函数
创建统一的格式化函数,确保分钟和秒数都显示为两位数(比如将1:3转为1:03):
String formatDuration(Duration? duration) { if (duration == null) return "0:00"; String twoDigits(int n) => n.toString().padLeft(2, '0'); final minutes = twoDigits(duration.inMinutes.remainder(60)); final seconds = twoDigits(duration.inSeconds.remainder(60)); return "$minutes:$seconds"; }
3. 优化UI中的时长显示
使用工具函数替换硬编码的格式化逻辑,同时处理duration为空的情况:
// 已播放时长 Text( formatDuration(Duration(seconds: value.toInt())), style: const TextStyle(color: Colors.white), ), // 总时长 Text( formatDuration(duration), style: const TextStyle(color: Colors.white), ),
4. 移除播放按钮中冗余的duration获取
initPlayer已经在初始化时获取了总时长,播放按钮中无需重复执行:
onTap: () async { if (isPlaying) { await player.pause(); setState(() => isPlaying = false); } else { await player.resume(); setState(() => isPlaying = true); } },
完整修改后的代码
import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; import 'dart:ui'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return const MaterialApp( home: HomePage(), ); } } class HomePage extends StatefulWidget { const HomePage({Key? key}) : super(key: key); @override State<HomePage> createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { String img_cover_url = "https://i.pinimg.com/736x/a7/a9/cb/a7a9cbcefc58f5b677d8c480cf4ddc5d.jpg"; bool isPlaying = false; double value = 0; final player = AudioPlayer(); Duration? duration; // 初始化为null,避免默认0值干扰 Duration? position; // 统一格式化时长 String formatDuration(Duration? duration) { if (duration == null) return "0:00"; String twoDigits(int n) => n.toString().padLeft(2, '0'); final minutes = twoDigits(duration.inMinutes.remainder(60)); final seconds = twoDigits(duration.inSeconds.remainder(60)); return "$minutes:$seconds"; } void initPlayer() async { await player.setSource(AssetSource("forest.mp3")); duration = await player.getDuration(); setState(() {}); // 刷新UI显示总时长 player.onPositionChanged.listen((Duration d) { setState(() { value = d.inSeconds.toDouble(); }); }); } @override void initState() { super.initState(); initPlayer(); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ Container( constraints: const BoxConstraints.expand(), decoration: const BoxDecoration( image: DecorationImage( image: AssetImage("assets/cover.jpg"), fit: BoxFit.cover, ), ), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 28, sigmaY: 28), child: Container(color: Colors.black.withOpacity(0.6)), ), ), Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ClipRRect( borderRadius: BorderRadius.circular(30.0), child: Image.asset("assets/cover.jpg", width: 250.0), ), const SizedBox(height: 10.0), const Text( "Summer", style: TextStyle(color: Colors.white, fontSize: 36, letterSpacing: 6), ), const SizedBox(height: 50.0), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( formatDuration(Duration(seconds: value.toInt())), style: const TextStyle(color: Colors.white), ), const SizedBox(width: 10), Expanded( child: Slider.adaptive( onChanged: (v) { setState(() { value = v; }); }, min: 0.0, max: duration?.inSeconds.toDouble() ?? 0.0, value: value.clamp(0.0, duration?.inSeconds.toDouble() ?? 0.0), onChangeEnd: (newValue) async { setState(() { value = newValue; }); await player.seek(Duration(seconds: newValue.toInt())); if (isPlaying) await player.resume(); }, activeColor: Colors.white, ), ), const SizedBox(width: 10), Text( formatDuration(duration), style: const TextStyle(color: Colors.white), ), ], ), const SizedBox(height: 60.0), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(60.0), color: Colors.black87, border: Border.all(color: Colors.white38), ), width: 50.0, height: 50.0, child: InkWell( onTapDown: (details) => player.setPlaybackRate(0.5), onTapUp: (details) => player.setPlaybackRate(1), child: const Center(child: Icon(Icons.fast_rewind_rounded, color: Colors.white)), ), ), const SizedBox(width: 60.0), Container( width: 50.0, height: 50.0, decoration: BoxDecoration( borderRadius: BorderRadius.circular(60.0), color: Colors.black87, border: Border.all(color: Colors.pink), ), child: InkWell( onTap: () async { if (isPlaying) { await player.pause(); setState(() => isPlaying = false); } else { await player.resume(); setState(() => isPlaying = true); } }, child: Icon(isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.white), ), ), const SizedBox(width: 60.0), Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(60.0), color: Colors.black87, border: Border.all(color: Colors.white38), ), width: 50.0, height: 50.0, child: InkWell( onTapDown: (details) => player.setPlaybackRate(2), onTapUp: (details) => player.setPlaybackRate(1), child: const Center(child: Icon(Icons.fast_forward_rounded, color: Colors.white)), ), ), ], ) ], ), ], ), ); } }
额外优化说明
- 将
duration初始值改为null,配合格式化函数避免初始显示错误 - 给Slider添加
Expanded包裹,解决布局溢出问题 - 限制Slider的value范围,防止超出总时长
- 简化播放按钮逻辑,移除冗余的重复操作
内容的提问来源于stack exchange,提问作者Bella Lee
相关产品推荐
相关产品推荐

