【问题标题】:Flutter Riverpod : return 2 stream with StreamProviderFlutter Riverpod:使用 StreamProvider 返回 2 个流
【发布时间】:2021-08-02 21:19:49
【问题描述】:

我有使用包Asset Audio PlayerFlutter Riverpod 作为状态管理的音乐播放器应用程序。我想听两件事:

  1. 收听歌曲的当前时长
  2. 收听当前播放的歌曲
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'),
                );
               
              },
            ),

【问题讨论】:

  • 您应该将答案添加为答案,而不是作为问题的编辑。
  • 另外,我的回答没有用吗?它解决了您对使用 rxdart 的解决方案提出的担忧。
  • @AlexHartford 你的回答和我预期的一样,我只是在我的问题中添加了另一种方法。
  • @AlexHartford 我对我的案例的最佳实践感到好奇,最好像您的方法那样分离 StreamProvider ,或者我们可以使用我的方法吗?我的问题的重点是,它更多是关于代码效率
  • 我不能保证我的答案是“最佳实践”,但它是解决问题的持久、易于理解的方法。

标签: flutter dart riverpod


【解决方案1】:

我会首先为每个Stream 创建一个StreamProvider

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();
});

【讨论】:

    【解决方案2】:

    这个答案是@AlexHartford 所做的另一种方法。

    我的案例目前的解决方案是使用包rxdartCombineLatestStream.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使用的时候,我要知道返回结果的顺序。

    【讨论】:

      猜你喜欢
      • 2021-03-19
      • 2020-07-25
      • 2021-10-20
      • 1970-01-01
      • 2020-08-17
      • 2021-10-03
      • 2022-11-28
      • 1970-01-01
      • 2020-11-04
      相关资源
      最近更新 更多