Search code examples
flutterdartclassbloc

check if class instance forwarded by reference is member of abstract class


Using BLoC as state management, I want to check if an event forwarded to another widget belongs to a certain BLoC abstract class. The reason is that I am using a form containing widget for 2 different use cases / BLoCs and depending on the BLoC I need to use a different event. There is also a minor other difference between the use cases which is why I would like to identify the use case by the event passed when calling the widget. Sure, I could also pass a bool with that info, but then thought why not recognizing the abstract class of the event.

So let's assume I have

abstract class EditEvent extends Equatable {
  const EditEvent();
}

class EditItemDeselected extends EditEvent {
  EditItemDeselected(this.outerIdx, this.innerIdx);
  final int outerIdx, innerIdx;
  @override
  List<Object?> get props => [outerIdx, innerIdx];
}

abstract class EditMasterEvent extends Equatable {
  const EditMasterEvent();
}

class EditMasterItemDeselected extends EditMasterEvent {
  EditMasterItemDeselected(this.outerIdx, this.innerIdx);
  final int outerIdx, innerIdx;
  @override
  List<Object?> get props => [outerIdx, innerIdx];
}

Then, in some widget I call the child widget with

ChildWidget(itemSelectedEvent: (int i, int j) => EditItemDeselected(i, j))

with the parameter in ChildWidget declared as final Function(int, int) itemSelectedEvent;

Now, inside ChildWidget something like if (itemSelectedEvent is EditEvent) does not work, neither does if (itemSelectedEvent is EditItemDeselected). It always results in false.

The debugger shows for itemSelectedEvent the value {_Closure} Closure: (int, int) => EditItemDeselected


Solution

  • Since you declared itemSelectedEvent as dynamic Function(int, int) itemSelectedEvent is EditEvent will always return false since itemSelectedEvent is a Function and not an EditEvent.

    You need to call the function to get the return type. If you set itemSelectedEvent to (int i, int j) => EditItemDeselected(i, j) itemSelectedEvent(1, 2) is EditEvent will return true.

    Note: type checking is considered a bad practice in OOP. Because the switch cases need to be duplicated accross the code and if you add new types you need to adjust all of them.

    class ChildWidget {
      ChildWidget(this.itemSelectedEvent);
    
      final Function(int, int) itemSelectedEvent;
    
      void func(int i1, int i2) {
        if (itemSelectedEvent(i1, i2) is EditEvent) {
          // do if EditEvent
        }
        if (itemSelectedEvent(i1, i2) is EditMasterEvent ) {
          // do if EditMasterEvent
        }
      }
    }
    

    Consider this instead:

    abstract class EditEvent extends Equatable {
      const EditEvent();
    
      void doStuff();
    }
    
    class EditItemDeselected extends EditEvent {
      EditItemDeselected(this.outerIdx, this.innerIdx);
      final int outerIdx, innerIdx;
    
      void doStuff() {
        // do if EditEvent
      }
    
      @override
      List<Object?> get props => [outerIdx, innerIdx];
    }
    
    class EditMasterItemDeselected extends EditEvent {
      EditMasterItemDeselected(this.outerIdx, this.innerIdx);
      final int outerIdx, innerIdx;
    
      void doStuff() {
        // do if EditMasterEvent
      }
    
      @override
      List<Object?> get props => [outerIdx, innerIdx];
    }
    
    class ChildWidget {
      ChildWidget(this.itemSelectedEvent);
    
      final Function(int, int) itemSelectedEvent;
    
      void func(int i1, int i2) {
        itemSelectedEvent(i1, i2).doStuff()
      }
    }