EggBender
EggBender

Reputation: 1020

Why isn't my sub class that implements/extends a parent class recognized as a sub type of the parent class in Dart Flutter?

I have the ProjectModel class which implements IIdentified class. But during runtime, I get an error that ProjectModel is not a sub type of IIdentified although it implements it. I have also tried to use extend instead of implements in ProjectModel but that didn't help either. How can I work with interfaces in Dart Flutter so that a Widget can have a generic type that fullfills a certain interface or contract?

Parent class IIdentified I use like an interface:

abstract class IIdentified {
  String id;
  String name;
}

Sub class implementing (also tried extending) parent class IIdentified:


class ProjectModel implements IIdentified {
  String id;
  DateTime created;
  DateTime updated;
  bool hasMileage;
  bool hasAllowance;
  bool hasTravelTime;
  String name;
  String description;
  DateTime startDate;
  DateTime endDate;
  DateTime completed;
  double estimatedTime;
  double absentTime;
  double presentTime;
  Status status;
  String companyId;
  String customerId;

  ProjectModel(
      {this.id,
      this.created,
      this.updated,
      this.hasMileage,
      this.hasAllowance,
      this.hasTravelTime,
      this.name,
      this.description,
      this.startDate,
      this.endDate,
      this.completed,
      this.estimatedTime,
      this.absentTime,
      this.presentTime,
      this.status,
      this.companyId,
      this.customerId});

I get the following error that the ProjectModel parameter in a function I'm trying to pass, does not count as being an IIdentified:

The following _TypeError was thrown building LoaderDropdownButton<ProjectModel>(dirty, dependencies: [MediaQuery], state: LoaderDropdownButtonState<IIdentified>#f2031):
type '(ProjectModel) => void' is not a subtype of type '(IIdentified) => void'

The generic widget only accepting type T extends IIdentified:

class LoaderDropdownButtonState<T extends IIdentified> extends State<LoaderDropdownButton<T>> {
  double displayWidth;
  double displayHeight;

  @override
  void initState() {
    init();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    setDisplayDimensions();
    return Container(
      padding: EdgeInsets.only(left: 25),
      color: widget.enableSelect ? themeConfig.dropButtonBg : themeConfig.dropButtonDisabledBg,
      child: Theme(
        data: ThemeData(canvasColor: Color(0xFFC5C5C5)),
        child: DropdownButtonHideUnderline(
          child: new DropdownButton<T>(
              hint: Row(
                children: <Widget>[
                  Expanded(
                    flex: 10,
                    child: Container(
                      alignment: Alignment.center,
                      margin: EdgeInsets.only(top: 2),
                      child: Text(
                        widget.text,
                        overflow: TextOverflow.ellipsis,
                        style: TextStyle(
                          color: widget.enableSelect
                              ? themeConfig.dropButtonFg
                              : themeConfig.dropButtonDisabledFg,
                          fontSize: displayWidth * 0.045,
                        ),
                      ),
                    ),
                  ),
                  Flexible(
                    flex: 2,
                    child: Container(
                      margin: EdgeInsets.only(top: 5),
                      child: Icon(
                        Icons.keyboard_arrow_down,
                        color: widget.enableSelect
                            ? themeConfig.dropButtonFg
                            : themeConfig.dropButtonDisabledFg,
                      ),
                    ),
                  )
                ],
              ),
              iconSize: 0,
              isExpanded: true,
              items: buildItems(),
              onChanged: widget.onChange),
        ),
      ),
    );
  }

  void init() {
    displayWidth = 1;
    displayHeight = 1;
  }

  setDisplayDimensions() {
    if (displayWidth == 1) displayWidth = MediaQuery.of(context).size.width;
    if (displayHeight == 1) displayHeight = MediaQuery.of(context).size.height;
  }

  List<DropdownMenuItem<T>> buildItems() {
    return widget.data.map((dataItem) {
      return DropdownMenuItem<T>(
        value: dataItem,
        child: Text(dataItem.name),
      );
    }).toList();
  }
}

Why doesn't ProjectModel pass as an IIdentified when it implements/extends it? How do you work with interfaces/contracts in generic widgets?

Thanks!

Upvotes: 3

Views: 1003

Answers (1)

julemand101
julemand101

Reputation: 31209

I get the following error that the ProjectModel parameter in a function I'm trying to pass, does not count as being an IIdentified.

I don't think you actually pasted that function but I can properly still guess what happen. The problem is that you have made a function with the signature:

(ProjectModel) => void

And want to use it as argument where it takes a (IIdentified) => void. This is a common problem when we are talking inheritance and method arguments since it can seem a little backward.

But, the problem is that if you have a method which can take a IIdentified as argument, it should be able to take all kind of IIdentified and not just a specific kind of IIdentified. You cannot just give a function which can handle ProjectModel and assume it would be able to handle some other subclass of IIdentified.

If we are instead was going to have the following:

() => ProjectModel

Then this would be a valid subtype of () => IIdentified since we are allowed to return a more specific kind of IIdentified.

Upvotes: 2

Related Questions