问题描述
我有使用包 Asset Audio Player 和 Flutter Riverpod 作为状态管理的音乐播放器应用程序。我想听两件事:
- 收听当前歌曲的时长
- 收听当前播放的歌曲
final currentSongPosition = StreamProvider.autodispose<Map<String,dynamic>>((ref) async* {
final AssetsAudioPlayer player = ref.watch(globalAudioPlayers).state;
ref.ondispose(() => player.dispose());
final Stream<double> _first = player.currentPosition.map((event) => event.inSeconds.todouble());
final Stream<double> _second =
player.current.map((event) => event?.audio.duration.inSeconds.todouble() ?? 0.0);
final maps = {};
maps['currentDuration'] = _first;
maps['maxDurationSong'] = _second;
return maps; << Error The return type 'Map<dynamic,dynamic>' isn't a 'Stream<Map<String,dynamic>>',as required by the closure's context.
});
如何将 2 个流返回到 1 个 StreamProvider
然后我可以简单地使用:
Consumer(
builder: (_,watch,__) {
final _currentSong = watch(currentSongPosition);
return _currentSong.when(
data: (value) {
final _currentDuration = value['currentDuration'] as double;
final _maxDuration = value['maxDuration'] as double;
return Text('Current : $_currentDuration,Max :$_maxDuration');
},loading: () => Center(child: CircularProgressIndicator()),error: (error,stackTrace) => Text('Error When Playing Song'),);
},),
解决方法
我首先为每个 StreamProvider
创建一个 Stream
:
final currentDuration = StreamProvider.autoDispose<double>((ref) {
final player = ref.watch(globalAudioPlayers).state;
return player.currentPosition.map((event) => event.inSeconds.toDouble());
});
final maxDuration = StreamProvider.autoDispose<double>((ref) {
final player = ref.watch(globalAudioPlayers).state;
return player.current.map((event) => event?.audio.duration.inSeconds.toDouble() ?? 0.0);
});
接下来,创建一个 FutureProvider
以读取每个 Stream
的最后一个值。
final durationInfo = FutureProvider.autoDispose<Map<String,double>>((ref) async {
final current = await ref.watch(currentDuration.last);
final max = await ref.watch(maxDuration.last);
return {
'currentDuration': current,'maxDurationSong': max,};
});
最后,创建一个将 StreamProvider
转换为 durationInfo
的 Stream
。
final currentSongPosition = StreamProvider.autoDispose<Map<String,double>>((ref) {
final info = ref.watch(durationInfo.future);
return info.asStream();
});
,
这个答案是@AlexHartford 所做的另一种方法。
我的案例当前的解决方案是使用包 rxdart CombineLatestStream.list()
,然后应该查看此代码:
流提供者
final currentSongPosition = StreamProvider.autoDispose((ref) {
final AssetsAudioPlayer player = ref.watch(globalAudioPlayers).state;
final Stream<double> _first = player.currentPosition.map((event) => event.inSeconds.toDouble());
final Stream<double> _second =
player.current.map((event) => event?.audio.duration.inSeconds.toDouble() ?? 0.0);
final tempList = [_first,_second];
return CombineLatestStream.list(tempList);
});
如何使用:
Consumer(
builder: (_,watch,__) {
final players = watch(globalAudioPlayers).state;
final _currentSong = watch(currentSongPosition);
return _currentSong.when(
data: (value) {
final _currentDuration = value[0];
final _maxDuration = value[1];
return Slider.adaptive(
value: _currentDuration,max: _maxDuration,onChanged: (value) async {
final newDuration = Duration(seconds: value.toInt());
await players.seek(newDuration);
context.read(currentSongProvider).setDuration(newDuration);
},);
},loading: () => const Center(child: CircularProgressIndicator()),error: (error,stackTrace) => Text(error.toString()),);
},),
但是这种方法有一个缺点,代码难以阅读。特别是在Consumer
中使用时,我必须知道返回结果的顺序。