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

Flutter如何从AnimatedSwitcher子组件内部修改状态实现切换动画

问题根因

AnimatedSwitcher 只有在自身 build 阶段检测到子组件的key或runtimeType发生变化时,才会触发新旧子组件的过渡动画。你的代码存在两个核心问题导致动画不生效:

  1. 控制切换的index是全局变量,且点击答案时调用的是子组件QuestionWidget的setState,仅刷新子组件自身,不会触发持有AnimatedSwitcher的父组件Test重新构建,AnimatedSwitcher完全感知不到子组件需要替换
  2. 问答切换的状态和触发逻辑放在子组件内部,违背了状态持有原则:谁渲染动画组件,谁就应该持有控制动画切换的状态。
可行实现方式

不需要更换组件,AnimatedSwitcher完全可以满足问答整体参与动画的需求,只需要做3处调整:

  • 将index状态从全局变量移动到持有AnimatedSwitcher的_TestState中统一管理
  • 给QuestionWidget新增答案点击回调参数,点击答案按钮时不直接修改index,而是调用父组件传入的回调
  • 在父组件的回调中执行setState更新index,让AnimatedSwitcher感知到子组件变化,自动触发新旧问答模块的过渡动画

如果后续需要支持滑动切换、自定义切换方向等更复杂的交互,再考虑替换为PageView结合自定义转场动画即可,当前场景无需额外引入组件。

修正后完整代码
import 'package:flutter/material.dart';

class Test extends StatefulWidget {
  const Test({Key? key}) : super(key: key);

  @override
  State<Test> createState() => _TestState();
}

class _TestState extends State<Test> {
  // 将index状态移动到AnimatedSwitcher所在的父组件管理
  int index = 0;

  // 问答数据统一放在父组件管理,避免全局变量
  final questionList = [
    Question("What is your favorite color?", ["red", "blue", "green"]),
    Question("How do you do today?", ["great", "not so well"]),
    Question("Do you like Flutter", ["yes", "no"]),
  ];

  // 统一处理答案点击切换逻辑
  void _onAnswerTap() {
    final isLastIndex = index == questionList.length - 1;
    setState(() => index = isLastIndex ? 0 : index + 1);
  }

  @override
  Widget build(BuildContext context) {
    var size = MediaQuery.of(context).size;
    return Scaffold(
        body: Center(
            child: SizedBox(
                width: size.width * 0.7,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: [
                    AnimatedSwitcher(
                        duration: const Duration(milliseconds: 400),
                        transitionBuilder: (child, animation) =>
                            SlideTransition(
                                position: Tween<Offset>(
                                        begin: const Offset(0.0, -1.0),
                                        end: const Offset(0.0, 0.0))
                                    .animate(animation),
                                child: FadeTransition(
                                    opacity: animation, child: child)),
                        // 每次index更新生成带新key的问答组件,传入点击回调
                        child: QuestionWidget(
                          key: ValueKey(index),
                          question: questionList[index],
                          onAnswerSelected: _onAnswerTap,
                        )
                    ),
                  ],
                ))));
  }
}

class QuestionWidget extends StatefulWidget {
  final Question question;
  // 新增答案点击回调参数
  final VoidCallback? onAnswerSelected;

  const QuestionWidget({
    required this.question,
    this.onAnswerSelected,
    Key? key,
  }) : super(key: key);

  @override
  State<QuestionWidget> createState() => _QuestionWidgetState();
}

class _QuestionWidgetState extends State<QuestionWidget> {
  @override
  Widget build(BuildContext context) {
    return Column(children: [
      Text(
        widget.question.questionText,
        style: const TextStyle(
          fontSize: 25,
        ),
      ),
      Wrap(
        direction: Axis.horizontal,
        alignment: WrapAlignment.center,
        spacing: 5.0,
        children: [
          for (var i in widget.question.answers)
            ElevatedButton(
                child: Text(i.toString()),
                onPressed: widget.onAnswerSelected
            )
        ],
      )
    ]);
  }
}

class Question {
  String questionText;
  List<String> answers;

  Question(this.questionText, this.answers);
}

扩展提示:如果你需要自定义旧组件退出的动画方向(比如旧题向上滑出、新题从下滑入),可以在transitionBuilder中通过判断animation.status区分进入/退出组件,分别配置不同的位移动画参数即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:30:57