flutter:倒计时功能与setState()的使用

  1. 代码
  2. setState() 里只放最小必要的状态更新,而不是流程逻辑。

代码

class _SplashPageState extends State<SplashPage> {
  final int duration = 10;
  int number = 0;
  // 倒计时
  Future<void> _countdown() async {
    number = duration;
    // initState 方法只会执行一次,我刚开始没有用循环,我以为setState刷新页面之后,又会执行setState
    for (int i = 0; i < duration; i++) {
      await Future.delayed(const Duration(seconds: 1), () {
        // mounted 这个值是用来判断当前状态对象是否仍然在树中
        // flutter 分配完你的组件树位置,会设置 mounted 为 true。
        // 如果 mounted 为 false,说明组件已经被从树中移除
        // mounted 是在什么时候被赋值为true的? 是在initState中
        if (mounted == true) {
          setState(() {
            print("setState: $number");
            number--;
          });
        }
      });
      // 倒计时结束, 进入 welcome
      if (number == 0) {
        // 这里不再调用setState方法,就不会再刷新页面
        print("倒计时结束");
      }
    }
  }

  @override
  void initState() {
    super.initState();
    // 启动倒计时
    _countdown();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: AppColors.backgroundSplash,
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
    				//.................这里省略代码.....................
            const SizedBox(height: 27),
            Text(
              number > 0 ? "$number" : "done",
              style: TextStyle(
                fontSize: 19,
                fontFamily: 'Poppins',
                fontWeight: FontWeight.bold,
                color: Colors.white,
                height: 22 / 19,
              ),
            ),
          ],
        ),
      ),
    );
  }
}
// 日志:
I/flutter (11576): setState: 10
D/InsetsController(11576): hide(ime(), fromIme=false)
I/ImeTracker(11576): com.example.flutter_quickstart_learn:ce680350: onCancelled at PHASE_CLIENT_ALREADY_HIDDEN
I/flutter (11576): setState: 9
I/flutter (11576): setState: 8
I/flutter (11576): setState: 7
I/flutter (11576): setState: 6
I/flutter (11576): setState: 5
I/flutter (11576): setState: 4
I/flutter (11576): setState: 3
I/flutter (11576): setState: 2
I/flutter (11576): setState: 1
I/flutter (11576): 倒计时结束

setState() 里只放最小必要的状态更新,而不是流程逻辑。

我犯的错误:

setState(() async {
  await Future.delayed(Duration(seconds: 1));
  counter--;
});

为什么不建议在 setState() 里写复杂逻辑?

  1. 影响可读性setState() 的职责应该很单一:修改状态并触发重建。如果里面塞了大量逻辑(比如网络请求、延时、计算),别人看代码时很难一眼看出哪个是 UI 状态的变更。

  2. 异步问题:在 setState() 里直接写 awaitFuture.delayed,容易出现 状态更新过晚Widget 已经销毁还在更新 的问题。

    setState(() async {
      await Future.delayed(Duration(seconds: 1));
      counter--;
    });
    

    这样写会报错:setState() callback argument returned a Future

  3. 性能问题:大量逻辑放在 setState() 里会导致 UI 重建的范围扩大,增加不必要的渲染。


正确写法:把耗时/逻辑操作写在外面,只在 setState() 里更新数据

Future<void> increaseLater() async {
  await Future.delayed(Duration(seconds: 1));
  if (mounted) {   // 确保页面还在
    setState(() {
      counter--;
    });
  }
}

×

喜欢就点赞,疼爱就打赏