Search code examples
dartdart-async

Using a stream for PropertyChanges


Trying to understand how streams work so i wrote this

class ViewModelBase{
   final List<PropertyChangedRecord> _changeRecords = new List<PropertyChangedRecord>();
   Stream<PropertyChangedRecord> _changes;

   Stream<PropertyChangedRecord> get changes{
     //lazy initialization
     if(_changes==null)
       _changes = new Stream<PropertyChangedRecord>.fromIterable(_changeRecords);
     return _changes;
   }

  _raisePropertyChanged(oldValue,newValue,propertySymbol){
    if(oldValue!=newValue){
      _changeRecords.add(new PropertyChangedRecord(this, propertySymbol,oldValue,newValue));
    }
    return newValue;
  }
}

class PropertyChangedRecord{
  final ViewModelBase viewModel;
  final Symbol propertySymbol;
  final Object newValue;
  final Object oldValue;
  PropertyChangedRecord(this.viewModel,this.propertySymbol,this.oldValue,this.newValue);
}

and used it as

void main() {
  var p = new Person('waa',13);
  p.age = 33334;
  p.name = 'dfa';
  p.changes.listen((p)=>print(p));
  p.age = 333834;
  p.name = 'dfia';
}

class Person extends ViewModelBase{
  String _name;
  String get name => _name;
  set name(String value) => _name = _raisePropertyChanged(_name,value,#name);

  int _age;
  int get age => _age;
  set age(int value) => _age = _raisePropertyChanged(_age,value,#age);

  Person(this._name,this._age);
}

and got the following exception

Uncaught Error: Concurrent modification during iteration: Instance(length:4) of '_GrowableList'

which i think is because the stream is removing items from the list while new PropertyChangedRecords are being added, how do i go around that?


Solution

  • The error could be caused by adding an item while the stream iterates the list.

    You could use a StreamController to create the stream instead (see How to pass a callback function to a StreamController for an example).

    class ViewModelBase{
       //final List<PropertyChangedRecord> _changeRecords = new List<PropertyChangedRecord>();
       //Stream<PropertyChangedRecord> _changes;
    
      final StreamController _changeRecords = new StreamController<PropertyChangedRecord>();
    
       Stream<PropertyChangedRecord> get changes => _changeRecords.stream;
    
      _raisePropertyChanged(oldValue,newValue,propertySymbol){
        if(oldValue!=newValue){
          _changeRecords.add(new PropertyChangedRecord(this, propertySymbol,oldValue,newValue));
        }
        return newValue;
      }
    }