Flutter iOS端onDurationChanged未触发问题求助
iOS端audioplayers网络音频onDurationChanged不触发的解决方案
问题概述
使用audioplayers: 1.1.0播放网络音频时,Android端能正常通过onDurationChanged获取时长,但iOS端该回调未被触发;本地音频在iOS端功能正常。
解决方案
1. 先设置音频源再播放(优先尝试)
iOS端直接调用play可能跳过元数据加载步骤,导致时长回调未触发。改为先通过setSource加载音频源,等待元数据就绪后再播放:
修改播放按钮的点击逻辑:
onPressed: () async { if (isPlaying) { player.pause(); } else { final source = UrlSource('https://www.kozco.com/tech/LRMonoPhase4.mp3'); // 先设置源,触发元数据加载 await player.setSource(source); // 再播放 await player.play(source); } },
2. 调整iOS音频会话配置
当前使用的AVAudioSessionCategory.ambient类别可能限制元数据获取,改为playback类别:
final AudioContext audioContext = AudioContext( iOS: AudioContextIOS( defaultToSpeaker: true, category: AVAudioSessionCategory.playback, // 修改此处 options: [ AVAudioSessionOptions.defaultToSpeaker, AVAudioSessionOptions.mixWithOthers, ], ), // Android配置保持不变 android: AudioContextAndroid( isSpeakerphoneOn: true, stayAwake: true, contentType: AndroidContentType.sonification, usageType: AndroidUsageType.assistanceSonification, audioFocus: AndroidAudioFocus.none, ), );
3. 升级audioplayers版本
1.1.0版本存在较多iOS端网络音频相关bug,升级至最新稳定版可解决大部分兼容性问题:
修改pubspec.yaml:
dependencies: audioplayers: ^3.0.1 # 替换为最新稳定版本
执行flutter pub get更新依赖后,重新测试。
4. 手动获取音频时长(备选方案)
若以上方法无效,可通过HTTP请求获取音频文件的Content-Length,结合音频码率计算时长(需服务端支持返回正确的Content-Length),或使用第三方库解析远程音频的元数据。
修改后的完整代码示例
import 'package:flutter/material.dart'; import 'package:audioplayers/audioplayers.dart'; import 'dart:async'; 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: Colors.blue, ), home: const MyHomePage(title: 'Audio Player'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { final player = AudioPlayer(); bool isPlaying = false; Duration duration = Duration.zero; Duration position = Duration.zero; String formatTime(int seconds) { return '${(Duration(seconds: seconds))}'.split('.')[0].padLeft(8, '0'); } final AudioContext audioContext = AudioContext( iOS: AudioContextIOS( defaultToSpeaker: true, category: AVAudioSessionCategory.playback, options: [ AVAudioSessionOptions.defaultToSpeaker, AVAudioSessionOptions.mixWithOthers, ], ), android: AudioContextAndroid( isSpeakerphoneOn: true, stayAwake: true, contentType: AndroidContentType.sonification, usageType: AndroidUsageType.assistanceSonification, audioFocus: AndroidAudioFocus.none, ), ); @override void initState() { super.initState(); AudioPlayer.global.setGlobalAudioContext(audioContext); player.onPlayerStateChanged.listen((state) { setState(() { isPlaying = state == PlayerState.playing; }); }); player.onDurationChanged.listen((newDuration) { setState(() { duration = newDuration; }); }); player.onPositionChanged.listen((newPosition) { setState(() { position = newPosition; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Slider( min: 0, max: duration.inSeconds.toDouble(), value: position.inSeconds.toDouble(), onChanged: (value) { final position = Duration(seconds: value.toInt()); player.seek(position); player.resume(); }, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ CircleAvatar( radius: 25, child: IconButton( icon: Icon( isPlaying ? Icons.pause : Icons.play_arrow, ), onPressed: () async { if (isPlaying) { player.pause(); } else { final source = UrlSource('https://www.kozco.com/tech/LRMonoPhase4.mp3'); await player.setSource(source); await player.play(source); } }, ), ), SizedBox( width: 20, ), CircleAvatar( radius: 25, child: IconButton( icon: const Icon(Icons.stop), onPressed: () { player.stop(); }, ), ), ], ), Container( padding: const EdgeInsets.all(20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text(formatTime(position.inSeconds)), Text(formatTime((duration - position).inSeconds)), ], ), ), ], ), ), ); } }
内容的提问来源于stack exchange,提问作者Anandh Krishnan
相关产品推荐
相关产品推荐

