mutate `currentState` property in flutter_bloc

4,792

Solution 1

The flutter bloc requires you to yield a new instance of the state, instead of modifying the currentState.

So I made the following changes in the below classes.

class Employee extends Equatable {
  int id;
  String name;
  double salery;

  Employee(this.id, this.name, this.salery) : super([id, name, salery]);

  Employee copyWith({
    String name,
    double salery,
  }) {
    return Employee(this.id, name ?? this.name, salery ?? this.salery);
  }

  @override
  String toString() {
    return 'Employee { id: $id, name: $name, salery: $salery }';
  }
}

Modified the mapEventToState method and added one helper method to update the list propertites.

@override
  Stream<List<Employee>> mapEventToState(EmployeeEvent event) async* {
    yield this.updateList(event);
  }

  Stream<EmployeeState> updateList(EmployeeEvent event) async* {
    final id = (currentState as EmployeeLoaded).employee[event.index].id;

    List<Employee> employeeUpdate =
        (currentState as EmployeeLoaded).employee.map((employee) {
      if (employee.id == id) {
        return employee.copyWith(
            salery: (event is IncrementEvent)
                ? employee.salery + 200
                : employee.salery - 200);
      }
      return employee;
    }).toList();

    print((currentState as EmployeeLoaded).employee == employeeUpdate);
    print(currentState == EmployeeLoaded(employeeUpdate));

    yield EmployeeLoaded(employeeUpdate);
  }

Solution 2

Your main problem is that you don't have any class that represent your state. So first you should create needed states. You can use simple examples provided in the bloc website.

And for the global understanding :

The BlocBuilder will rebuild only on these case (With equatable):

  • The instance runtime type of the new and the old state are different
  • The instance of the new and the old state are the same instance type it will deeply compare all nested properties of the previous and the new state to know what have changed by calling ==.
  • You can also use conditional property function that you provide on creation.

If you want to use the same instance you can extends Equatable (from equatable package here) or do the same thing it's doing. (this medium post can help you).

With your case you can do this :

abstract class EmployeeState extends Equatable {
  EmployeeState([List props = const []]) : super(props);

  @override
  String toString() => '$runtimeType{}';
}

class EmployeeLoaded extends EmployeeState{
  EmployeeLoaded(this.employees):super([employees])

  final List<Employee> employees;
}

For further info/help you can come on the bloc gitter.

Share:
4,792
Anirudha Mahale
Author by

Anirudha Mahale

Curious as to how these mobile apps worked the way they do, Almost immediately, I grabbed a chance and started off as an iOS mobile developer. And there it is, I knew this is what i wanted to do with my life! A Combo of creative and analytical skills gets me do better everyday. Definitely there is always more to learn and thats my passion. I have 5+ years of experience in designing, developing, project analysis and management in the field of mobile application development being iOS at my best.

Updated on December 12, 2022

Comments

  • Anirudha Mahale
    Anirudha Mahale over 1 year

    I have a flutter app in which I display list of employees with by their name and salery in listView widget. I have two buttons for each employee to increase or decrease their salery.

    I am using flutter_bloc for state management.

    Here is the Employee class.

    class Employee extends Equatable {
      int _id;
      String _name;
      double _salery;
    
      Employee(this._id, this._name, this._salery);
    
      // setters
      set id(int id) {
        this._id = id;
      }
    
      set name(String name) {
        this._name = name;
      }
    
      set salery(double salery) {
        this._salery = salery;
      }
    
      // getters
      int get id => this._id;
      String get name => this._name;
      double get salery => this._salery;
    }
    

    Here is the class as event.

    @immutable
    abstract class EmployeeEvent {
      final int _index;
      EmployeeEvent(this._index);
    
      int get index => this._index;
    }
    
    class IncrementEvent extends EmployeeEvent {
      IncrementEvent(int index) : super(index);
    }
    
    class DecrementEvent extends EmployeeEvent {
      DecrementEvent(int index) : super(index);
    }
    

    Here is the bloc class which handles the state.

    class EmployeeBloc extends Bloc<EmployeeEvent, List<Employee>> {
      @override
      List<Employee> get initialState => [
            Employee(1, 'Employee One', 10000.0),
            Employee(2, 'Employee Two', 10000.0),
            Employee(3, 'Employee Three', 10000.0),
            Employee(4, 'Employee Four', 10000.0),
            Employee(5, 'Employee Five', 10000.0),
          ];
    
      @override
      Stream<List<Employee>> mapEventToState(EmployeeEvent event) async* {
        // I have checked, the control reaches over here.
        double currentSalery = currentState[event.index].salery;
        if (event is IncrementEvent) {
          // Increments the salery when user presses the increment button
          currentState[event.index].salery = currentSalery + (currentSalery * 0.2);
        } else if (event is DecrementEvent) {
          currentState[event.index].salery = currentSalery - (currentSalery * 0.2);
        }
        print(currentState[event.index].salery); // even prints the incremented / decremented salery.
        yield currentState;
      }
    }
    

    After yielding, the BlocBuilder from the widget class isn't called.

    If I change the method like this then it calls BlocBuilder but I cann't increment or decrement the salery of employee at particular index.

    @override
    Stream<List<Employee>> mapEventToState(EmployeeEvent event) async* {
      if (event is IncrementEvent) {
        yield currentState;
      } else if (event is DecrementEvent) {
        yield currentState;
      }
    }
    

    How do I increment or decrement the salery of particular employee at particular index?