Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to reset BehaviorSubject #233

Closed
SAGARSURI opened this issue Jan 25, 2019 · 9 comments
Closed

How to reset BehaviorSubject #233

SAGARSURI opened this issue Jan 25, 2019 · 9 comments

Comments

@SAGARSURI
Copy link

SAGARSURI commented Jan 25, 2019

I am having an issue draining or closing the BehaviorSubject. It always given me the final value even after draining or closing it. How can I fix it? Here is my code:

link to the repo: https://github.com/SAGARSURI/Goals

This is my BLoC class. The _title and _goalMessage always pass the last stored value to the StreamBuilder:

class GoalsBloc {
  final _repository = Repository();
  final _title = BehaviorSubject<String>();
  final _goalMessage = BehaviorSubject<String>();
  final _showProgress = BehaviorSubject<bool>();

  Observable<String> get name => _title.stream.transform(_validateName);

  Observable<String> get goalMessage =>
      _goalMessage.stream.transform(_validateMessage);

  Observable<bool> get showProgress => _showProgress.stream;

  Function(String) get changeName => _title.sink.add;

  Function(String) get changeGoalMessage => _goalMessage.sink.add;

  final _validateMessage = StreamTransformer<String, String>.fromHandlers(
      handleData: (goalMessage, sink) {
    if (goalMessage.length > 10) {
      sink.add(goalMessage);
    } else {
      sink.addError(StringConstant.goalValidateMessage);
    }
  });

  final _validateName = StreamTransformer<String, String>.fromHandlers(
      handleData: (String name, sink) {
    if (RegExp(r'[!@#<>?":_`~;[\]\\|=+)(*&^%0-9-]').hasMatch(name)) {
      sink.addError(StringConstant.nameValidateMessage);
    } else {
      sink.add(name);
    }
  });

  void submit(String email) {
    _showProgress.sink.add(true);
    _repository
        .uploadGoal(email, _title.value, _goalMessage.value)
        .then((value) {
      _showProgress.sink.add(false);
    });
  }

  void extractText(var image) {
    _repository.extractText(image).then((text) {
      _goalMessage.sink.add(text);
    });
  }

  Stream<DocumentSnapshot> myGoalsList(String email) {
    return _repository.myGoalList(email);
  }

  Stream<QuerySnapshot> othersGoalList() {
    return _repository.othersGoalList();
  }

  //dispose all open sink
  void dispose() async {
    await _goalMessage.drain();
    _goalMessage.close();
    await _title.drain();
    _title.close();
    await _showProgress.drain();
    _showProgress.close();
  }

  //Convert map to goal list
  List mapToList({DocumentSnapshot doc, List<DocumentSnapshot> docList}) {
    if (docList != null) {
      List<OtherGoal> goalList = [];
      docList.forEach((document) {
        String email = document.data[StringConstant.emailField];
        Map<String, String> goals =
            document.data[StringConstant.goalField] != null
                ? document.data[StringConstant.goalField].cast<String, String>()
                : null;
        if (goals != null) {
          goals.forEach((title, message) {
            OtherGoal otherGoal = OtherGoal(email, title, message);
            goalList.add(otherGoal);
          });
        }
      });
      return goalList;
    } else {
      Map<String, String> goals = doc.data[StringConstant.goalField] != null
          ? doc.data[StringConstant.goalField].cast<String, String>()
          : null;
      List<Goal> goalList = [];
      if (goals != null) {
        goals.forEach((title, message) {
          Goal goal = Goal(title, message);
          goalList.add(goal);
        });
      }
      return goalList;
    }
  }

  //Remove item from the goal list
  void removeGoal(String title, String email) {
    return _repository.removeGoal(title, email);
  }
}
@frankpepermans
Copy link
Member

drain actually subscribes to the Subject, ignoring all values and only emitting if an Error is encountered.
whereas close effectively closes the Subject, disallowing any new events to be added.

You cannot reset a Subject/Stream, you need to create a new one instead which can receive its own series of events.

If you wish to discard the value which is in a Subject, after it was closed, try using:
var valueWhenOpen = subject.isClosed ? null : subject.value;

@SAGARSURI
Copy link
Author

@frankpepermans
Now I am doing something like this:

var _title = BehaviorSubject<String>();
  var _goalMessage = BehaviorSubject<String>();
  var _showProgress = BehaviorSubject<bool>();

  Observable<String> get name {
    if(_title == null){
      _title = BehaviorSubject<String>();
    }
    return _title.stream.transform(_validateName);
  }

  Observable<String> get goalMessage {
    if(_goalMessage==null){
      _goalMessage = BehaviorSubject<String>();
    }
    return _goalMessage.stream.transform(_validateMessage);
  }

  Observable<bool> get showProgress {
    if(_showProgress == null){
      _showProgress = BehaviorSubject<bool>();
    }
    return _showProgress.stream;
  }
//dispose all open sink
  void dispose() {
    _showProgress.close();
    _showProgress = null;
    _goalMessage.close();
    _goalMessage = null;
    _title.close();
    _title = null;
  }

It's working perfectly. But is this approach correct?

@frankpepermans
Copy link
Member

Any reason why you are using BehaviorSubject?

I assume you recreate the subjects because you don't want the last event to trigger immediately?

Maybe try a regular PublishSubject or StreamController, and return a deferred Stream in the getters instead?

@SAGARSURI
Copy link
Author

I am using BehaviorSubject to get the final value of the text entered by user in the Stream. As user will be adding some message in text field. My intention is to validate that message and upload the final text to the server.

@SAGARSURI
Copy link
Author

@frankpepermans
Any suggestions or approach for the above use case?

@frankpepermans
Copy link
Member

Well, you could always bundle regular Subjects, not Behavior, using combineLatest? Then submit whenever the onClick Stream of the submit button fires?

@frankpepermans
Copy link
Member

closing this (not an issue)

perhaps move it to stack overflow for example?

@Sreejithtechno
Copy link

The operator '[]' isn't defined for the type 'Map<String, dynamic> Function()'.
Try defining the operator '[]'.

@hoc081098
Copy link
Collaborator

hoc081098 commented Aug 20, 2020

@Sreejithtechno more detail please 🥺

Sent from my Redmi 7A using FastHub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants