为什么我的通用 StatefulWidget class 在运行时会出现 TypeError?

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

我有一个具有 Function 回调的通用 StatefulWidget class。当我尝试调用该回调时,我得到一个运行时 TypeError:

══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following _TypeError was thrown building MyStatefulWidget<int>(dirty, state:
MyState<int>#cacb3):
type '(int) => Widget' is not a subtype of type '(dynamic) => Widget'

The relevant error-causing widget was:
  MyStatefulWidget<int>
  MyStatefulWidget:file:///path/to/my_flutter_project/lib/main.dart:11:13

When the exception was thrown, this was the stack:
#0      MyState.build (package:my_flutter_project/main.dart:33:19)

可重现的例子:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

Widget f(int n) => Text('$n');

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyStatefulWidget<int>(callback: foo, value: 42),
    );
  }
}

class MyStatefulWidget<T> extends StatefulWidget {
  final Widget Function(T) callback;
  final T value;

  const MyStatefulWidget({
    required this.callback,
    required this.value,
    Key? key,
  }) : super(key: key);

  @override
  MyState<T> createState() => MyState<T>();
}

class MyState<T> extends State<MyStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return widget.callback(widget.value);
  }
}

我已经尝试将 MyStatefulWidget 显式构造为 MyStatefulWidget<int>,但这没有帮助。 Widget Function(dynamic) 类型来自哪里?

MyState createState() => MyState(); 省略了类型参数,所以它 returns 一个 MyState,它是 shorthand for MyState<dynamic>.

此外,MyState<T> extends State<MyStatefulWidget> 对于 ... extends State<MyStatefulWidget<dynamic>> 是 shorthand, 而不是 ... extends State<MyStatefulWidget<T>>

MyState<T> 继承的 widget 成员的静态类型因此将为 MyStatefulWidget<dynamic>,而 widget.callback 的静态类型将为 Widget Function(dynamic) .在运行时,关联的 MyStatefulWidget 对象具有对 f 的引用(一个 Widget Function(int)。但是,不能将其视为 Widget Function(dynamic)(如 MyState<T> 所期望的那样)因为 f 不能接受所有 dynamic 参数,所以你在运行时得到一个 TypeError

底线

如果您有泛型 StatefulWidget,则必须在 StatefulWidget 引用其 State class 或 State指的是它的StatefulWidgetclass。常见错误是:

  • 忽略 createState 中的类型参数。
  • 在为相应的 State 声明继承时忽略类型参数 class。

所以:

class MyStatefulWidget<T> extends StatefulWidget {
  ...
  MyState createState() => MyState(); // WRONG
}

class MyState<T> extends State<MyStatefulWidget> { // WRONG
  ...
}

应该是:

class MyStatefulWidget<T> extends StatefulWidget {
  ...
  MyState<T> createState() => MyState<T>();
}

class MyState<T> extends State<MyStatefulWidget<T>>
  ...
}

strict_raw_types analysis option 有时可以帮助捕获此类错误,尽管当前的实现似乎只检查隐式 dynamic 类型参数并且似乎没有捕获类型参数受限制的情况(比如如果你有 MyStatefulWidget<T extends num>).