Я пытаюсь выполнить функцию обратного вызова, как только таймер заканчивается в определенном виджете, но я продолжаю получать это исключение:
I/flutter (16413): Another exception was thrown: setState() or markNeedsBuild() called during build.
Итак, у меня есть этот виджет под названием countdown
:
class Countdown extends StatefulWidget {
final VoidCallback onCountdownExpire;
Countdown(this.onCountdownExpire);
@override
CountdownState createState() => CountdownState();
}
class CountdownState extends State<Countdown> with TickerProviderStateMixin {
AnimationController controller;
String get timerString {
Duration duration = controller.duration * controller.value;
return '${duration.inMinutes}:${(duration.inSeconds % 60).toString()}';
}
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..addStatusListener((AnimationStatus status){
if (status == AnimationStatus.completed)
widget.onCountdownExpire();
});
controller.reverse(from: 1.0);
}
...
...
... // omitted code
}
Поэтому, как только анимация будет завершена, она вызовет функцию обратного вызова:
class _QuizPageState extends State<QuizPage> {
... // omitted code
@override
void initState() {
... // omitted code
}
void onCountdownExpire() {
setState(() {
_topContentImage = AssetImage(questions[questionNum++].imagePath);
});
}
... // omitted code
}
Я пытался следовать решение, но это не работает и дает мне то же исключение:
void onCountdownExpire() =>
setState(() {
_topContentImage = AssetImage(questions[questionNum++].imagePath);
});
Я также пробовал это, но безрезультатно:
@override
void initState() {
super.initState();
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
)..addStatusListener((AnimationStatus status) =>
(status == AnimationStatus.completed) ?
widget.onCountdownExpire():null
);
controller.reverse(from: 1.0);
}
возможно, попробуйте включить 'dart: async':
import 'dart:async';
затем попробуйте обернуть вызов функции onCountdownExpire
короткоживущим Timer()
:
...
Timer(
Duration(milliseconds:50),
() {
if (status == AnimationStatus.completed)
widget.onCountdownExpire();
},
);
...
это приведет к тому, что setState()
произойдет за пределами фазы сборки последнего кадра вашей анимации.
Ошибка возникает, скорее всего, из-за того, что Countdown()
перерисовывается как часть перерисовки виджета QuizPage()
. Добавление Timer()
заставит обновление происходить за пределами области build()
асинхронным способом и по-прежнему будет достигать тех же результатов без ошибки.
ты делал полный перезапуск? или просто горячая перезагрузка? поскольку это изменение было в методе initState(), вам потребуется полный перезапуск. кроме того, усеченное сообщение об ошибке («Другое исключение...») обычно означает, что предыдущее исключение было создано до этого. было бы полезно понять, что это за исключение, чтобы сузить проблему.
хорошо, я думаю, что знаю, почему выдается ошибка диапазона, потому что questions
имеет только 2 элемента и вызывает [2], что неправильно. И причина, по которой это происходит, заключается в том, что когда анимация начинается, она каким-то образом «завершается», поскольку я вставил отладочную печать непосредственно перед вызовом windget.onCountdownExpire()
, и она печатается, когда анимация начинается, а не когда она закончена.
я сделал полный перезапуск
хорошо, причина, по которой это было завершено в начале: controller.reverse(from: 1.0);
. После того, как я изменил его на controller.animateTo(1.0);
, он был завершен в конце анимации.
Я также мог бы использовать controller.dismissed
вместо controller.completed
, чтобы решить завершенную в начале проблему.
хорошо, я попробовал это и вместо этого получил это исключение
I/flutter (16413): Another exception was thrown: RangeError (index): Invalid value: Not in range 0..1, inclusive: 2