【问题标题】:setState() or markNeedsBuild() called during build exception prevents me from executing callback在构建异常期间调用的 setState() 或 markNeedsBuild() 阻止我执行回调
【发布时间】:2019-10-16 16:28:15
【问题描述】:

一旦计时器在特定小部件中结束,我正在尝试执行回调函数,但我不断收到此异常:

I/flutter (16413):引发了另一个异常:在构建期间调用了 setState() 或 markNeedsBuild()。

所以我有一个名为 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
}

我尝试关注solution,但它不起作用并给了我同样的例外:

  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);
  }

【问题讨论】:

    标签: flutter dart


    【解决方案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
    • 您是否进行了完全重启?还是只是热重载?因为此更改发生在 initState() 方法中,所以您需要完全重新启动。此外,消息的截断错误消息(“另一个异常......”)通常意味着在该异常之前引发了先前的异常。了解该异常是什么有助于缩小问题范围。
    • 好的,我想我知道为什么会抛出范围错误,因为questions 只有 2 个元素并且它正在调用 [2],这是错误的。发生这种情况的原因是,当动画开始时,它以某种方式“完成”了,因为我在调用 windget.onCountdownExpire() 之前插入了一个调试打印,它在动画开始时打印,而不是在动画结束时打印。
    • 我完全重启了
    • 好的,开始时状态已完成的原因:controller.reverse(from: 1.0);。在我将其更改为controller.animateTo(1.0); 后,它在动画结束时完成。
    猜你喜欢
    • 2021-08-07
    • 2021-08-08
    • 2020-02-25
    • 2018-05-15
    • 2020-10-05
    • 2020-12-12
    • 2020-12-28
    • 2021-01-06
    相关资源
    最近更新 更多