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

Flutter中如何实现圆形与矩形的出现缩放动画(按钮切换场景)

解决Flutter点击按钮切换形状并实现尺寸平滑过渡动画的问题

我来帮你梳理一下当前代码的问题,然后给出一个更简洁易理解的实现方案,完美满足你的需求:点击按钮切换圆形/矩形,新元素从小尺寸平滑过渡到目标尺寸,动画时长1秒。

你的代码存在的核心问题

  1. 子组件状态孤立:你把Circle和Rectangle做成了独立的StatefulWidget,但它们内部的_checked状态从未被触发改变,所以AnimatedContainer根本不会执行动画——因为没有状态变化来驱动它。
  2. 组件替换而非属性变化:你用if条件来切换显示不同的组件,这会直接移除旧组件、添加新组件,而AnimatedContainer的动画是基于同一个组件的属性变化来实现的,组件替换无法触发平滑过渡。

优化后的实现方案

我们可以把所有状态统一放在父组件MyButtonState中,用单个AnimatedContainer来完成形状、颜色和尺寸的动画过渡,这样逻辑更清晰,也更容易维护。

完整代码示例

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    debugShowCheckedModeBanner: false,
    home: Scaffold(
      body: MyButton(),
    ),
  ));
}

class MyButton extends StatefulWidget {
  @override
  MyButtonState createState() => MyButtonState();
}

class MyButtonState extends State<MyButton> {
  // 记录当前显示的形状:'circle'/'rectangle',null表示未显示任何形状
  String? _currentShape;
  // 控制AnimatedContainer的尺寸,初始为0(隐藏状态)
  double _shapeSize = 0;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(40),
      child: Column(
        children: <Widget>[
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: <Widget>[
              TextButton(
                onPressed: () {
                  // 先将尺寸重置为小尺寸,再切换形状并放大到目标尺寸
                  setState(() => _shapeSize = 100);
                  Future.delayed(Duration(milliseconds: 100), () {
                    setState(() {
                      _currentShape = 'circle';
                      _shapeSize = 300;
                    });
                  });
                },
                child: Text('Red Circle'),
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.red),
                  foregroundColor: MaterialStateProperty.all(Colors.white),
                ),
              ),
              TextButton(
                onPressed: () {
                  setState(() => _shapeSize = 100);
                  Future.delayed(Duration(milliseconds: 100), () {
                    setState(() {
                      _currentShape = 'rectangle';
                      _shapeSize = 300;
                    });
                  });
                },
                child: Text('Blue Rectangle'),
                style: ButtonStyle(
                  backgroundColor: MaterialStateProperty.all(Colors.blue),
                  foregroundColor: MaterialStateProperty.all(Colors.white),
                ),
              ),
            ],
          ),
          // 只有当_currentShape不为null时,才显示AnimatedContainer
          if (_currentShape != null)
            AnimatedContainer(
              width: _shapeSize,
              height: _shapeSize,
              decoration: BoxDecoration(
                // 根据当前形状切换颜色
                color: _currentShape == 'circle' ? Colors.red : Colors.blue,
                // 根据当前形状切换形状
                shape: _currentShape == 'circle' ? BoxShape.circle : BoxShape.rectangle,
              ),
              // 设置动画时长为1秒
              duration: Duration(seconds: 1),
              // 设置动画曲线,让过渡更自然
              curve: Curves.easeInOutCubic,
            ),
        ],
      ),
    );
  }
}

关键代码解释

  1. 状态管理:用_currentShape记录当前显示的形状,_shapeSize控制容器尺寸,所有状态都在父组件中统一管理,避免了子组件状态孤立的问题。
  2. 动画触发逻辑:点击按钮时,先将尺寸重置为小尺寸(100),延迟100毫秒后切换形状并将尺寸放大到目标尺寸(300)——这样无论是首次显示还是切换形状,都会有从小尺寸平滑过渡到目标尺寸的效果。
  3. AnimatedContainer的作用:只要它的属性(尺寸、颜色、形状等)发生变化,就会自动根据duration和curve执行平滑过渡动画,不需要额外的动画控制器,非常适合新手使用。

额外说明

如果你希望首次点击按钮时,形状从0直接放大到300,只需要把按钮点击事件里的代码改成:

onPressed: () {
  setState(() {
    _currentShape = 'circle';
    _shapeSize = 300;
  });
},

初始_shapeSize是0,点击后直接变300,同样会有平滑的放大动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 08:47:41