felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.8k stars 3.39k forks source link

Bloc is not updating list as well as state #2098

Closed rizirf-connect closed 3 years ago

rizirf-connect commented 3 years ago

Hey @felangel can you please tell me why there is no changes occur in the list

AudioPlayerBloc.dart

`initializePlayer: (e) async* {
        await audioPlayerStateSubscription?.cancel();
        audioPlayerStateSubscription =
            audioPlayer.onPlayerStateChanged.listen((playerState) => add(
                  AudioPlayerEvent.playerStateChanged(
                    playerStateIndex: playerState.index,
                  ),
                ));

        await audioPositionSubscription?.cancel();
        audioPositionSubscription = audioPlayer.onAudioPositionChanged.listen(
            (audioPosition) => add(
                AudioPlayerEvent.getCurrentPosition(position: audioPosition)));

        await audioLengthSubscription?.cancel();
        audioLengthSubscription = audioPlayer.onDurationChanged.listen(
          (audioDuration) => add(
            AudioPlayerEvent.getCurrentDuration(length: audioDuration),
          ),
        );
      },
getCurrentPosition: (e) async* {
        if (state.lastPlayingPara != Para.empty()) {
          final int index = List<AudioItem>.from(state.audioItems).indexWhere(
            (audioItem) => audioItem.para == state.lastPlayingPara,
          );
          final List<AudioItem> updatedList =
              List<AudioItem>.from(state.audioItems)
                ..elementAt(index).copyWith(audioPosition: e.position);
          yield state.copyWith(audioItems: updatedList);
        }
      },
      getCurrentDuration: (e) async* {
        if (state.lastPlayingPara != Para.empty() && state.isPlaying) {
          final int index = List<AudioItem>.from(state.audioItems).indexWhere(
            (audioItem) => audioItem.para == state.lastPlayingPara,
          );
          final List<AudioItem> updatedList =
              List<AudioItem>.from(state.audioItems)
                ..elementAt(index).copyWith(audioLength: e.length);
          yield state.copyWith(audioItems: updatedList);
        }
      },
`

AudioPlayerState.dart

@freezed
abstract class AudioPlayerState with _$AudioPlayerState {
  const AudioPlayerState._();
  factory AudioPlayerState.initial() => AudioPlayerState(
        isPlaying: false,
        isLoading: false,
        noteText: "",
        lastPlayingPara: Para.empty(),
        audioItems: paraList.toList(growable: true),
      );
  const factory AudioPlayerState({
    // @required AudioItems itemQueue,
    @required final bool isPlaying,
    @required final bool isLoading,
    @required final String noteText,
    @required final Para lastPlayingPara,
    @required final List<AudioItem> audioItems,
  }) = _AudioPlayerState;
}
jinyus commented 3 years ago

Try the code below for the getCurrentPosition event. If this works then use this approach approach with the other event.

NB: elementAt is inefficient for lists because it will traverse the list up to the index,it's supposed to be used for lazy iterables

getCurrentPosition: (e) async* {
     if (state.lastPlayingPara != Para.empty()) {

           //no need to create a copy just to get the index, also types are inferred
          final index = state.audioItems.indexWhere((e) => e.para == state.lastPlayingPara);

          //no copy needed here either.
          final newAudioItem = state.audioItems[index].copyWith(audioPosition: e.position);
          final itemsCopy = List<AudioItem>.from(state.audioItems);

          itemsCopy
             ..removeAt(index);
             ..insert(index, newAudioItem);

          yield state.copyWith(audioItems: itemsCopy);
        }
      },
rizirf-connect commented 3 years ago

Thanks @jinyus this is working fine but when i remove note from para the bloc builder does not rebuild state

removeNoteAt: (e) async* {
        final newPara = state.playlist.audioItems[lastPlayingItemIndex].para
          ..notes.removeAt(e.index);
        final newAudioItem = state.playlist.audioItems[lastPlayingItemIndex]
            .copyWith(para: newPara);
        final itemsCopy = List<AudioItem>.from(state.playlist.audioItems);

        itemsCopy
          ..removeAt(lastPlayingItemIndex)
          ..insert(lastPlayingItemIndex, newAudioItem);

        yield state.copyWith(playlist: AudioItems(audioItems: itemsCopy));
      }
jinyus commented 3 years ago

That's because you're modifying the old para. newPara in your snippet still references the old para. So create a new instance of the para.

final oldPara = state.playlist.audioItems[lastPlayingItemIndex].para;
final newParaNotes = oldPara.notes..removeAt(e.index);
final newPara = oldPara.copyWith(notes:newParaNotes);

That should work since newPara now references a new object. If this doesn't work trying making a copy of newParaNotes;

rizirf-connect commented 3 years ago

Thanks you @jinyus You are life saver man.