Почему я получаю TypeError во время выполнения с моим общим классом StatefulWidget?

avatar
jamesdlin
16 апреля 2022 в 19:49
101
1
0

У меня есть общий класс StatefulWidget с обратным вызовом Function. Когда я пытаюсь вызвать этот обратный вызов, я получаю время выполнения 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)?

Источник

Ответы (1)

avatar
jamesdlin
16 апреля 2022 в 19:49
0

MyState createState() => MyState(); опускает аргументы типа, поэтому возвращает MyState, что является сокращением для MyState<dynamic>.

Кроме того, MyState<T> extends State<MyStatefulWidget> является сокращением для ... extends State<MyStatefulWidget<dynamic>>, не для ... extends State<MyStatefulWidget<T>>, как предполагалось.

Поэтому статический тип члена MyState<T>, унаследованного от widget, будет MyStatefulWidget<dynamic>, а статический тип widget.callback будет Widget Function(dynamic). Во время выполнения связанный объект MyStatefulWidget имеет ссылку на f (a Widget Function(int)). Однако это нельзя рассматривать как Widget Function(dynamic) (как ожидается MyState<T>), поскольку f не может принимать все <17231738 аргументы, поэтому во время выполнения вы получите TypeError.

Итог

Если у вас есть универсальный StatefulWidget, вы должны явно и последовательно указать параметры типа везде, где StatefulWidget ссылается на свой класс State или где State ссылается на свой класс StatefulWidget. Распространенные ошибки:

  • Игнорирование параметров типа в createState.
  • Игнорирование параметров типа при объявлении наследования для соответствующего класса State.

Итак:

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 иногда может помочь отловить такие ошибки, хотя текущая реализация, по-видимому, проверяет только неявно заданные параметры типа dynamic и, похоже, не отлавливает случаи, когда параметр типа ограниченный (например, если у вас есть MyStatefulWidget<T extends num>).