You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter 如何在AnimatedSwitcher转场动画运行时禁用所有点击事件

问题解决:AnimatedSwitcher转场期间禁用点击异常修复

错误原因

transitionBuilder本身属于组件构建流程的执行环节,你直接在该方法内给动画添加状态监听器,监听器内同步调用setState会触发新一轮组件构建,和当前正在执行的构建流程冲突,就会抛出setState() or markNeedsBuild() called during build.异常。
同时原写法存在重复绑定监听器的隐藏问题:每次转场触发transitionBuilder执行时,都会给动画对象重复添加监听器,造成多余的性能开销。

方案1:动画状态监听修复版

将监听器绑定逻辑推迟到当前帧构建完成后执行,即可避免构建冲突,同时新增状态标记避免重复绑定监听器:

class _MainPageState extends State<MainPage> {
  int pageIndex = 1;
  bool disableClicks = false;
  // 记录上一次动画状态,避免重复绑定监听器
  AnimationStatus? _lastAnimStatus;

  @override
  Widget build(BuildContext context) {
    return AbsorbPointer(
      absorbing: disableClicks,
      child: Scaffold(
        body: AnimatedSwitcher(
          duration: 500.milliseconds,
          child: pageIndex == 0
              ? Container(color: Colors.red, key: UniqueKey())
              : pageIndex == 1
              ? Container(color: Colors.blue, key: UniqueKey())
              : Container(color: Colors.green, key: UniqueKey()),
          transitionBuilder: (child, animation) {
            // 把监听器绑定逻辑放到当前帧构建完成后执行
            WidgetsBinding.instance.addPostFrameCallback((_) {
              if (animation.status == _lastAnimStatus) return;
              _lastAnimStatus = animation.status;
              
              animation.addStatusListener((status) {
                _lastAnimStatus = status;
                switch (status) {
                  case AnimationStatus.forward:
                  case AnimationStatus.reverse:
                    if (mounted && !disableClicks) {
                      setState(() => disableClicks = true);
                    }
                    break;
                  case AnimationStatus.dismissed:
                  case AnimationStatus.completed:
                    if (mounted && disableClicks) {
                      setState(() => disableClicks = false);
                    }
                    break;
                }
              });
            });
            return FadeTransition(opacity: animation, child: child);
          },
        ),
      ),
    );
  }
}

方案2:更简洁的预标记方案

如果你的转场时长是固定值,不需要动态适配动画时长,可以直接在切换页面的逻辑里提前标记禁用点击,等动画时长结束后自动恢复,不需要监听动画状态,实现更简单:

// 页面切换逻辑示例
void switchPage(int targetIndex) {
  // 已禁用点击或目标页和当前页相同直接返回
  if (disableClicks || pageIndex == targetIndex) return;
  setState(() {
    disableClicks = true;
    pageIndex = targetIndex;
  });
  // 动画时长到后恢复点击
  Future.delayed(500.milliseconds, () {
    if (mounted) setState(() => disableClicks = false);
  });
}

内容的提问来源于stack exchange,提问作者Saman Salehi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.12 04:30:04