10

I'm getting a runtime error from a Dart generic function:

 Widget card = widget.cardBuilder(item); 

Which generates:

 type '(Contact) => Widget' is not a subtype of type '(DeletableItem) => Widget' 

The Contact class is defined as

class Contact implements DeletableItem { 

I then have a method:

 class UndoableListView<T extends DeletableItem> { List<T> children; final Widget Function(T item) cardBuilder; 

And this is where the runtime error occurs

 Widget buildItem(BuildContext context, int index) { T item = children[index]; Widget card = widget.cardBuilder(item); <<<<< error thrown here. 

I'm clearly mis-understanding something with how generics work.

Contact clearly extends DeleteableItem.

So what have I done wrong?

4
  • The issue is not that Contact doesn't implement DeletableItem, but rather that the whole function itself has a different signature since (Contact) => Widget cannot be cast to (DeletableItem) => Widget. Without seeing more code, it's hard to propose a fix. Can you show the context in which buildItem is being used? Commented Aug 1, 2019 at 5:04
  • The code is extracted from a rather large app. The intent is to pass in a list of Deleteable item to a list and have the list callback the builder to generate a card from the item. I'm confused as to way it can't be cast given that the Contact is of type Deleteable? Commented Aug 1, 2019 at 7:17
  • Any updates on this? I am getting the same error, I dont understand how Contract is not a subtype of DeletablItem even tho, well, it is! Commented Jun 22, 2021 at 22:06
  • Nothing here says that Contact is not a subtype of DeletableItem, but that the function type Widget Function(Contact) is not a subtype of the function type Widget Function(DeletableItem). Function types are contravariant in their parameter types (a function type can only a subtype of another function type if the parameter types of the first function are supertypes of the parameter types of the second function). So, this is working as intended. Commented Jan 19, 2022 at 23:52

1 Answer 1

1

Contact extends DeletableItem, and is a subtype of it.

Functions are contravariant in their parameters, so

Widget Function(Contact) 

is not a subtype of

Widget Function(DeletableItem) 

In fact, it's the other way around.

Functions act that way because subtyping means substitutability. You can use a Contact anywhere a DeletableItem is needed (because it implements the entire DeletableItem interface), so Contact is a subtype of DeletableItem.

You can use a Widget Function(DeletableItem) anywhere a Widget Function(Contact) is needed, because you can call that function with any Contact (and it returns a Widget as expected). So Widget Function(DeletableItem) is a subtype of Widget Function(Contact).

Dart generics are covariant, even though it's not always sound.

That means that a UndoableListView<Contact> is a subtype of UndoableListView<DeletableItem>.

The problem comes up because the cardBuilder field uses the class type parameter contravariantly in the function type,

 final Widget Function(T item) cardBuilder; 

So, when you do:

UndoableListView<DeletableItem> list = UndoableListView<Contact>(); var builder = list.cardBuilder; 

you get an error. The list.cardBuilder expression has a static type of Widget Function(DeletableItem), but a run-time type of Widget Function(Contact), which is not a subtype of the static type. That's a type soundness problem, one the compiler is aware of, and it inserts a type check to ensure that the builder variable won't end up with an invalidly typed value.

And that check throws.

Sign up to request clarification or add additional context in comments.

1 Comment

How to fix this error?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.