Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dart / Flutter subclassing to use optional parameters

I am trying to figure out how to create a subclass of a class without specifying all the optional parameters of the parent but still have access to them from the Constructor of the child subclass. This is especially important when subclassing Flutter Widgets with the myriad of attributes.

E.g. DoorWidget is parent class with many optional parameters.

ChildDoorWidget inherits DoorWidget to add extra logic, but still wants all the optional parent parameters without having to specify every parameter in the super() method of the subclass.

Is there some way of doing this?

Here is an example.

    // Maine widget
    class DoorWidget {
      String color = 'red';
      int width;
      int height;
      int p1;
      int p2;
      int p3;
      Function onKicked = () => print('Kicked');

      DoorWidget(this.color, {this.onKicked, this.width, this.height, this.p1, this.p2, this.p3});
    }

    class ChildDoorWidget extends DoorWidget {
      @override
      String color = 'green';
      ChildDoorWidget(String color, {Function onKicked, int width, int height})
          // Do I need to specify every parent optional parameter in the super class?
          // Is there a way to avoid this.
          : color = color,
            super(color, onKicked: onKicked, width: width);
    }

    main() {

      DoorWidget d = DoorWidget('green', width: 10, onKicked: () => print('DoorWidget Kicked') );
      print('Text Class');
      print(d.color);
      print(d.width);
      d.onKicked();

      ChildDoorWidget c = ChildDoorWidget('blue',
          width: 12, onKicked: () => print('ChildDoorWidget tapped called'));
      // Ideally I would like to do this:
      //  ChildDoorWidget m = ChildDoorWidget('blue', width: 12, onKicked: () => print('tapped called'), p1: 1, p2: 2, and any other optional params);
      print('\nMyText Class');
      print(c.color);
      print(c.width);
      c.onKicked();
    }
like image 736
user603749 Avatar asked May 15 '19 16:05

user603749


1 Answers

Actually there's no direct and simple way to call a constructor with parameters that you have not defined on it.

You wanted to call:

ChildDoorWidget m = ChildDoorWidget('blue', width: 12, 
                      onKicked: () => print('tapped called'),
                      p1: 1, p2: 2);

But since p1 and p2 aren't defined on ChildDoorWidget's constructor you get the error "The named parameter isn't defined.".

A workaround is to remember that p1 and p2 can be accessed by ChildDoorWidget and with cascade notation you still can set their values on the same statement that you call the constructor:

ChildDoorWidget m = ChildDoorWidget('blue', width: 12, 
                      onKicked: () => print('tapped called'))
                      ..p1=1..p2=2;

You could do the same for all, except the required parameters (color in this example)

ChildDoorWidget m = ChildDoorWidget('blue')
                   ..width = 12
                   ..onKicked = (() => print('tapped called'))
                   ..p1 = 1
                   ..p2 = 2;

Remember that constructor's code is fully executed before cascade assignments be performed. So, use this workaround only for constructors that perform no immediate operations with it's parameters, like in your example.

this same logic can be applied to your question about specifying parent's optional parameters in the super class.

Observation: initialization values for color and onKicked on DoorWidget are useless because constructor overrides these values. This is valid for color on ChildDoorWidget.

The following is a revision of the initial code with the above implementations:

void show(dynamic d) {
  print('\ncolor=${d.color}\nwidth=${d.width}\nheight=${d.height}\np1=${d.p1}\np2=${d.p2}\np3=${d.p3}\nonKicked=${d.onKicked}');
  d.onKicked == null ? '' : d.onKicked();
}

class DoorWidget {
  String color;
  int width;
  int height;
  int p1;
  int p2;
  int p3;
  Function onKicked;
  DoorWidget(this.color,
      {this.onKicked, this.width, this.height, this.p1, this.p2, this.p3});
}

class ChildDoorWidget extends DoorWidget {
  @override
  String color = 'orange'; //initialization useless because constructor overrides it with "this.color"

  ChildDoorWidget(String this.color) : super(color);
}

main() {
  print('\n\n\n create DoorWidget with color, width and onKicked');
  DoorWidget d = DoorWidget('green',
      width: 10, onKicked: () => print('DoorWidget Kicked'));
  show(d);

  print('\n\n\n create ChildDoorWidget with color, width and onKicked');
  ChildDoorWidget c = ChildDoorWidget('blue')
    ..width = 12
    ..onKicked = () => print('ChildDoorWidget tapped called');
  show(c);

  print('\n\n\n create ChildDoorWidget with many parameters');
  ChildDoorWidget m = ChildDoorWidget('yellow')
    ..width = 12
    ..onKicked = (() => print('tapped called'))
    ..p1 = 1
    ..p2 = 2;
  show(m);
}
like image 175
Nelson Rodrigues Avatar answered Sep 30 '22 02:09

Nelson Rodrigues