'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 |
