SetState() или markNeedsBuild(), вызываемые во время исключения сборки, не позволяют мне выполнить обратный вызов

Я пытаюсь выполнить функцию обратного вызова, как только таймер заканчивается в определенном виджете, но я продолжаю получать это исключение:

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);
  }
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
798
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

возможно, попробуйте включить 'dart: async':

import 'dart:async';

затем попробуйте обернуть вызов функции onCountdownExpire короткоживущим Timer():

...
        Timer(
          Duration(milliseconds:50),
          () {
            if (status == AnimationStatus.completed)
              widget.onCountdownExpire();
          },
        );
...

это приведет к тому, что setState() произойдет за пределами фазы сборки последнего кадра вашей анимации.

Ошибка возникает, скорее всего, из-за того, что Countdown() перерисовывается как часть перерисовки виджета QuizPage(). Добавление Timer() заставит обновление происходить за пределами области build() асинхронным способом и по-прежнему будет достигать тех же результатов без ошибки.

хорошо, я попробовал это и вместо этого получил это исключение I/flutter (16413): Another exception was thrown: RangeError (index): Invalid value: Not in range 0..1, inclusive: 2

Rajdeep 31.05.2019 05:19

ты делал полный перезапуск? или просто горячая перезагрузка? поскольку это изменение было в методе initState(), вам потребуется полный перезапуск. кроме того, усеченное сообщение об ошибке («Другое исключение...») обычно означает, что предыдущее исключение было создано до этого. было бы полезно понять, что это за исключение, чтобы сузить проблему.

loushou 31.05.2019 05:32

хорошо, я думаю, что знаю, почему выдается ошибка диапазона, потому что questions имеет только 2 элемента и вызывает [2], что неправильно. И причина, по которой это происходит, заключается в том, что когда анимация начинается, она каким-то образом «завершается», поскольку я вставил отладочную печать непосредственно перед вызовом windget.onCountdownExpire(), и она печатается, когда анимация начинается, а не когда она закончена.

Rajdeep 31.05.2019 06:01

я сделал полный перезапуск

Rajdeep 31.05.2019 06:02

хорошо, причина, по которой это было завершено в начале: controller.reverse(from: 1.0);. После того, как я изменил его на controller.animateTo(1.0);, он был завершен в конце анимации.

Rajdeep 31.05.2019 06:07

Я также мог бы использовать controller.dismissed вместо controller.completed, чтобы решить завершенную в начале проблему.

Rajdeep 31.05.2019 06:16

Другие вопросы по теме