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

Flutter实现Widget切换时的渐隐渐显过渡动画问题

嘿,这个Widget切换的渐隐渐显顺序动画问题我刚好多有研究,给你两个实用的解决方案,你可以根据需求选:

方案一:用AnimatedOpacity分步控制动画(直观易定制)

这个方案的思路是先让加载组件渐隐,等动画完成后再让内容组件渐显,通过状态变量和Future.delayed来精准控制时序。

修改后的完整代码如下:

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {
  bool _isLoading = true;
  bool _showContent = false;
  double _loadingOpacity = 1.0;
  double _contentOpacity = 0.0;
  // 统一设置动画时长,方便后续调整
  final _animationDuration = Duration(milliseconds: 500);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Theme.of(context).scaffoldBackgroundColor,
      child: Stack(
        alignment: Alignment.center,
        children: [
          // 加载组件:控制渐隐动画
          if (_isLoading)
            AnimatedOpacity(
              opacity: _loadingOpacity,
              duration: _animationDuration,
              child: SizedBox(
                width: 24,
                height: 24,
                child: GestureDetector(
                  onTap: _toggleWidget,
                  child: CircularProgressIndicator(),
                ),
              ),
            ),
          // 内容组件:控制渐显动画
          if (_showContent)
            AnimatedOpacity(
              opacity: _contentOpacity,
              duration: _animationDuration,
              child: GestureDetector(
                onTap: _toggleWidget,
                child: Text(
                  "WELCOME",
                  style: TextStyle(fontSize: 24),
                ),
              ),
            ),
        ],
      ),
    );
  }

  void _toggleWidget() async {
    if (_isLoading) {
      // 第一步:让加载组件渐隐
      setState(() {
        _loadingOpacity = 0.0;
      });
      // 等待渐隐动画完成
      await Future.delayed(_animationDuration);
      // 第二步:切换状态,显示内容组件并渐显
      setState(() {
        _isLoading = false;
        _showContent = true;
        _contentOpacity = 1.0;
      });
    } else {
      // 反向切换:先让内容组件渐隐
      setState(() {
        _contentOpacity = 0.0;
      });
      await Future.delayed(_animationDuration);
      // 再显示加载组件并渐显
      setState(() {
        _showContent = false;
        _isLoading = true;
        _loadingOpacity = 1.0;
      });
    }
  }
}

方案二:用AnimatedSwitcher实现顺序动画(简洁高效)

AnimatedSwitcher是Flutter专门用于Widget切换动画的原生组件,我们可以通过自定义transitionBuilder来控制动画时序,实现先隐后显的效果:

import 'package:flutter/material.dart';

class MyWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _MyWidgetState();
  }
}

class _MyWidgetState extends State<MyWidget> {
  bool _isLoading = true;
  // 总动画时长:前半段旧组件渐隐,后半段新组件渐显
  final _totalDuration = Duration(milliseconds: 1000);

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Theme.of(context).scaffoldBackgroundColor,
      child: AnimatedSwitcher(
        duration: _totalDuration,
        // 自定义动画时序:前50%时间旧组件渐隐,后50%时间新组件渐显
        transitionBuilder: (child, animation) {
          final isLoadingWidget = child.key == const ValueKey('loading');
          // 旧组件(加载)的渐隐动画:0-50%时间完成
          final outAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
            CurvedAnimation(
              parent: animation,
              curve: const Interval(0.0, 0.5, curve: Curves.easeOut),
            ),
          );
          // 新组件(内容)的渐显动画:50%-100%时间完成
          final inAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
            CurvedAnimation(
              parent: animation,
              curve: const Interval(0.5, 1.0, curve: Curves.easeIn),
            ),
          );

          return FadeTransition(
            opacity: isLoadingWidget ? outAnimation : inAnimation,
            child: child,
          );
        },
        // 必须给每个切换的Widget设置唯一key,AnimatedSwitcher才能识别切换动作
        child: _isLoading
            ? SizedBox(
                key: const ValueKey('loading'),
                width: 24,
                height: 24,
                child: GestureDetector(
                  onTap: _toggleWidget,
                  child: CircularProgressIndicator(),
                ),
              )
            : GestureDetector(
                key: const ValueKey('content'),
                onTap: _toggleWidget,
                child: const Text(
                  "WELCOME",
                  style: TextStyle(fontSize: 24),
                ),
              ),
      ),
    );
  }

  void _toggleWidget() {
    setState(() {
      _isLoading = !_isLoading;
    });
  }
}

两个方案的对比

  • 方案一更适合需要精细控制动画步骤的场景(比如中间要插入其他业务逻辑),代码可读性高,新手容易理解和修改。
  • 方案二更简洁,利用Flutter原生组件的能力,适合大多数常规的顺序切换需求,代码量更少,维护成本低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 08:51:40