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

Flutter中容器内容变化时如何实现尺寸平滑动画效果

问题根因

ScaleTransition布局时会直接采用子组件的原始尺寸计算自身占用空间,不会随缩放值动态调整大小,即使缩放值为0,也会提前占满Cancel文本的宽度,导致Row提前撑开、出现空白占位。

修复方案

替换ScaleTransition为SizeTransition实现横向展开动画,配合AnimatedContainer让按钮尺寸和文本显示同步过渡,不会出现提前占位的问题。

实现代码

首先在State中混入TickerProviderStateMixin,初始化两个动画控制器分别控制图标旋转、文本展开:

class SyncCancelButton extends StatefulWidget {
  const SyncCancelButton({super.key});

  @override
  State<SyncCancelButton> createState() => _SyncCancelButtonState();
}

class _SyncCancelButtonState extends State<SyncCancelButton> with TickerProviderStateMixin {
  late final AnimationController _rotationController;
  late final AnimationController _expandController;
  bool _isSyncing = false;

  @override
  void initState() {
    super.initState();
    _rotationController = AnimationController(
      vsync: this,
      duration: const Duration(seconds: 1),
    );
    _expandController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
  }

  @override
  void dispose() {
    _rotationController.dispose();
    _expandController.dispose();
    super.dispose();
  }

  void _handleTap() {
    setState(() {
      _isSyncing = !_isSyncing;
      if (_isSyncing) {
        _rotationController.repeat();
        _expandController.forward();
      } else {
        _rotationController.stop();
        _expandController.reverse();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        padding: const EdgeInsets.all(5.0),
        decoration: BoxDecoration(
          color: Colors.white.withOpacity(0.1),
          border: Border.all(
            color: Colors.pink.withOpacity(0.6),
          ),
          borderRadius: BorderRadius.circular(5.0),
        ),
        child: Row(
          mainAxisSize: MainAxisSize: MainAxisSize.min,
          children: [
            RotationTransition(
              turns: _rotationController,
              child: const Icon(
                Icons.sync,
                color: Colors.pink,
              ),
            ),
            SizeTransition(
              sizeFactor: _expandController,
              axis: Axis.horizontal,
              axisAlignment: -1,
              child: Row(
                children: const [
                  SizedBox(width: 5),
                  Text(
                    "Cancel",
                    style: TextStyle(
                      color: Colors.pink,
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  SizedBox(width: 5),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}
实现效果
  • 初始状态仅展示同步图标,无多余空白占位
  • 点击按钮后,按钮宽度随Cancel文本平滑横向展开,同步图标开始旋转
  • 再次点击触发取消,按钮平滑收缩回初始尺寸,图标停止旋转

如果需要增加文本淡入效果,可以在SizeTransition的子组件外层嵌套FadeTransition,不要单独使用ScaleTransition包裹文本,否则会再次出现提前占位问题。

内容的提问来源于stack exchange,提问作者Sanketh B. K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:27:04