'Generic parameter type in Dart? [duplicate]

I'm trying to create a class that use a generic type as a parameter in a callback that returns some subtype of Flutter's Widget. Here's what I started with:

    class Subscriber<P extends PublishingController> extends StatefulWidget {
      const Subscriber({required this.builder, Key? key}) : super(key: key);

      final Widget Function(P) builder;

      @override
      _SubscriberState<P> createState() => _SubscriberState<P>();
    }

    class _SubscriberState<P extends PublishingController> extends State<Subscriber> {
      final P publisher = GetIt.instance.get<P>();

      @override
      void initState() {
        publisher.subscribe(rebuild);
        super.initState();
      }

      @override
      Widget build(BuildContext context) {
        return widget.builder(publisher);
      }

      @override
      void dispose() {
        publisher.unsubscribe(rebuild);
        super.dispose();
      }

      void rebuild() {
        setState(() {});
      }
    }

... with the Publisher:

    mixin Publisher {
      List<Function> subscribers = <void Function()>[];

      void subscribe(Function f) {
          subscribers.add(f);
      }

      void unsubscribe(Function f) {
        subscribers.remove(f);
      }

      void publish() {
        for (var f in subscribers) {
          f();
        }
      }
    }

    class PublishingController with Publisher {}

... and how I called it:

  child: Subscriber<MapController>(
             builder: (controller) => Column(...

... with:

  class MapController extends PublishingController {...

... but that gives me the error:

  ======== Exception caught by widgets library =======================================================
  The following _TypeError was thrown building Subscriber<MapController>(dirty, state: _SubscriberState<MapController>#d7e05):
  type '(MapController) => Column' is not a subtype of type '(PublishingController) => Widget'

I think I'm specifying the parameter type through the generics, and a function can return a subtype of its return type— what am I getting wrong here?

EDIT:

I got it working, but I'm not putting this in as an answer— I don't understand what the problem was, or why this version works; I changed my Subscriber class to:

    abstract class Builder<P extends PublishingController> extends StatefulWidget {
      const Builder({required this.builder, Key? key}) : super(key: key);
      final Widget Function(P) builder;
    }

    class Subscriber<P extends PublishingController> extends Builder<P> {
      const Subscriber({required builder, Key? key}) : super(builder: builder, key: key);

      @override
      _SubscriberState<P> createState() => _SubscriberState<P>();
    }

If someone wants to explain why this change would make the difference, please put it in an answer, and I'll gladly accept it.



Solution 1:[1]

Your _SubscriberState<P> class extends State<Subscriber>, which in your case is shorthand for State<Subscriber<PublishingController>>, not for State<Subscriber<P>>.

The static type of the _SubscriberState<P>'s inherited widget member therefore will be Subscriber<PublishingController>, and the static type of widget.builder will be Widget Function(PublishingController). At runtime, the associated Subscriber object has a reference to a Column Function(MapController) object. However, that cannot be treated as a Widget Function(PublishingController) since it does not accept all PublishingController arguments, so you end up with a runtime error.

See: Why am I getting TypeError at runtime with my generic StatefulWidget class?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1