MyState createState() => MyState();
omits the type arguments, so it returns a MyState
, which is shorthand for MyState<dynamic>
.
Additionally, MyState<T> extends State<MyStatefulWidget>
is shorthand for ... extends State<MyStatefulWidget<dynamic>>
, not for ... extends State<MyStatefulWidget<T>>
as intended.
The static type of the MyState<T>
‘s inherited widget
member therefore will be MyStatefulWidget<dynamic>
, and the static type of widget.callback
will be Widget Function(dynamic)
. At runtime, the associated MyStatefulWidget
object has a reference to f
(a Widget Function(int)
. However, that cannot be treated as Widget Function(dynamic)
(as expected by MyState<T>
) since f
cannot accept all dynamic
arguments, so you end up with a TypeError
at runtime.
Bottom line
If you have a generic StatefulWidget
, you must explicitly and consistently supply the type parameters everywhere that the StatefulWidget
refers to its State
class or where the State
refers to its StatefulWidget
class. Common mistakes are:
- Neglecting type parameters in
createState
. - Neglecting type parameters when declaring inheritance for the corresponding
State
class.
So:
class MyStatefulWidget<T> extends StatefulWidget {
...
MyState createState() => MyState(); // WRONG
}
class MyState<T> extends State<MyStatefulWidget> { // WRONG
...
}
instead should be:
class MyStatefulWidget<T> extends StatefulWidget {
...
MyState<T> createState() => MyState<T>();
}
class MyState<T> extends State<MyStatefulWidget<T>>
...
}
The strict_raw_types
analysis option sometimes can help catch such mistakes, although the current implementation seems to check only for implicitly dynamic
type parameters and doesn’t seem to catch cases where the type parameter is restricted (such as if you have MyStatefulWidget<T extends num>
).