Flutter 教程(十四)动画

在 Flutter 中,动画分为两类,分别是补间动画、物理动画。

  • 补间动画:补间动画是一种预先定义物体运动的起点和终点,物体的运动方式,运动时间,时间曲线,然后从起点过渡到终点的动画。
  • 物理动画:基于物理的动画是一种模拟现实世界运动的动画,通过建立运动模型来实现。例如一个篮球 从高处落下,需要根据其下落高度,重力加速度,地面反弹力等影响因素来建立运动模型。

补间动画

需要注意,Flutter 中的补间动画和Android中的补间动画是不一样的。Flutter 中的补间动画会改变对象的属性,而不仅仅是视觉上UI的变化。

在 Flutter 中,补间动画分为以下几种类型:

  • 隐式动画和显示动画
  • Hero动画
  • 交织动画

隐式动画和显示动画

在 Flutter 中,以 *Transition 命名,比如 FadeTransition 、SizeTransition 和 RotationTransition 等,需要我们自己定义和操作 AnimationController的动画叫做显示动画

而以 Animated* 开头的 Widget,例如 AnimatedPositionedAnimatedContainerAnimatedPaddingAnimatedOpacity 等控件,它们最大的特点就是内部已经完全封装好逻辑,你只需要配置对应参数就可以触发动画。这种动画叫做隐式动画

实际上,隐式动画 就是 显示动画 封装后的产物,因此隐式动画和显示动画他们的称呼不怎么重要。

  • 隐式动画示例如下:
scala 复制代码
class ImplicitAnimationWidget extends StatefulWidget {
  const ImplicitAnimationWidget({super.key});

  @override
  _ImplicitAnimationWidgetState createState() => _ImplicitAnimationWidgetState();
}

class _ImplicitAnimationWidgetState extends State<ImplicitAnimationWidget> {
  bool _isBig = false;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        AnimatedContainer(
          width: _isBig ? 200 : 100,
          height: _isBig ? 200 : 100,
          color: _isBig ? Colors.blue : Colors.red,
          duration: const Duration(seconds: 1),
          curve: Curves.easeInOut,
        ),
        const SizedBox(height: 20),
        ElevatedButton(
          onPressed: () {
            setState(() {
              _isBig = !_isBig;
            });
          },
          child: const Text('Toggle Size and Color'),
        ),
      ],
    );
  }
}

效果如下图所示:

  • 显示动画示例如下:
less 复制代码
class RotationAinmationPage extends StatefulWidget {
  @override
  _RotationAinmationPageState createState() => _RotationAinmationPageState();
}

class _RotationAinmationPageState extends State<RotationAinmationPage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _turns;
  bool _playing = false;

  // 控制动画运行状态
  void _toggle() {
    if (_playing) {
      _playing = false;
      _controller.stop();
    } else {
      _controller.forward()..whenComplete(() => _controller.reverse());
      _playing = true;
    }
    setState(() {});
  }

  @override
  void initState() {
    super.initState();
    // 初始化动画控制器,设置动画时间
    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 10),
    );

    // 设置动画取值范围和时间曲线
    _turns = Tween(begin: 0.0, end: 3.1415926 * 2).animate(
      CurvedAnimation(parent: _controller, curve: Curves.easeIn),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('显示动画')),
      body: Center(
        child: RotationTransition(
          // 传入动画值
          turns: _turns,
          child: Container(
            width: 200,
            height: 200,
            child: FlutterLogo(),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _toggle,
        child: Icon(_playing ? Icons.pause : Icons.play_arrow),
      ),
    );
  }
}

效果如下图所示:

Hero 动画

Hero 指的是可以在路由(页面)之间"飞行"的 widget,简单来说 Hero 动画就是在路由切换时,有一个共享的widget 可以在新旧路由间切换。由于共享的 widget 在新旧路由页面上的位置、外观可能有所差异,所以在路由切换时会从旧路逐渐过渡到新路由中的指定位置,这样就会产生一个 Hero 动画。

代码示例如下:

less 复制代码
class FirstPage extends StatelessWidget {
  const FirstPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('First Page'),),
      body: Center(
        child: GestureDetector(
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => const SecondPage(),
              ),
            );
          },
          child: Hero(
            tag: 'heroImage',
            child: Image.asset(
              'assets/images/test.png', // 请替换为你自己的图片路径
              width: 200,
              height: 200,
            ),
          ),
        ),
      ),
    );
  }
}
class SecondPage extends StatelessWidget {
  const SecondPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Second Page'),
      ),
      body: Align(
        alignment: Alignment.topCenter,
        child: Hero(
          tag: 'heroImage',
          child: Image.asset(
            'assets/images/test.png', // 请替换为你自己的图片路径
            width: 400,
            height: 400,
          ),
        ),
      ),
    );
  }
}

效果如下图所示:

交织动画

交织动画(Staggered Animation)是指多个动画按照一定的顺序和时间间隔依次或重叠播放。代码示例如下,代码来源9.5 交织动画 | 《Flutter实战·第二版》

less 复制代码
class StaggerAnimation extends StatelessWidget {
  StaggerAnimation({Key? key, required this.controller}) : super(key: key) {
    //高度动画
    height = Tween<double>(begin: .0, end: 300.0).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(
          0.0,
          0.6, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    color = ColorTween(begin: Colors.green, end: Colors.red).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(
          0.0,
          0.6, //间隔,前60%的动画时间
          curve: Curves.ease,
        ),
      ),
    );

    padding = Tween<EdgeInsets>(
      begin: const EdgeInsets.only(left: .0),
      end: const EdgeInsets.only(left: 100.0),
    ).animate(
      CurvedAnimation(
        parent: controller,
        curve: const Interval(
          0.6,
          1.0, //间隔,后40%的动画时间
          curve: Curves.ease,
        ),
      ),
    );
  }

  late final Animation<double> controller;
  late final Animation<double> height;
  late final Animation<EdgeInsets> padding;
  late final Animation<Color?> color;

  Widget _buildAnimation(BuildContext context, child) {
    return Container(
      alignment: Alignment.bottomCenter,
      padding: padding.value,
      child: Container(color: color.value, width: 50.0, height: height.value),
    );
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(builder: _buildAnimation, animation: controller);
  }
}

class StaggerRoute extends StatefulWidget {
  @override
  _StaggerRouteState createState() => _StaggerRouteState();
}

class _StaggerRouteState extends State<StaggerRoute>
    with TickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      duration: const Duration(milliseconds: 2000),
      vsync: this,
    );
  }

  _playAnimation() async {
    try {
      //先正向执行动画
      await _controller.forward().orCancel;
      //再反向执行动画
      await _controller.reverse().orCancel;
    } on TickerCanceled {
      //捕获异常。可能发生在组件销毁时,计时器会被取消。
    }
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        children: [
          ElevatedButton(
            onPressed: () => _playAnimation(),
            child: Text("start animation"),
          ),
          Container(
            width: 300.0,
            height: 300.0,
            decoration: BoxDecoration(
              color: Colors.black.withOpacity(0.1),
              border: Border.all(color: Colors.black.withOpacity(0.5)),
            ),
            //调用我们定义的交错动画Widget
            child: StaggerAnimation(controller: _controller),
          ),
        ],
      ),
    );
  }
}

效果如下图所示:

物理动画

物理动画的实现可以看 Widget 的物理模拟动画效果 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter

参考

相关推荐
测试人社区—66793 分钟前
提升测试覆盖率的有效手段剖析
人工智能·学习·flutter·ui·自动化·测试覆盖率
子春一1 小时前
Flutter 与 AI 融合开发实战:在移动端集成大模型、智能推荐与生成式 UI
人工智能·flutter·ui
克喵的水银蛇4 小时前
Flutter 通用底部弹窗:ActionSheetWidget 一键实现自定义选项与交互
flutter
小a彤4 小时前
Flutter 深度解析:跨平台开发的终极利器
flutter
_大学牲4 小时前
Flutter 勇闯2D像素游戏之路(二):绘制加载游戏地图
flutter·游戏·游戏开发
程序员老刘4 小时前
千万别再纠结Flutter状态管理,90%项目根本不需要选
flutter·客户端
renxhui5 小时前
Flutter 常用组件全属性说明(持续更新中)
flutter
m0_看见流浪猫请投喂5 小时前
Flutter鸿蒙化现有三方插件兼容适配鸿蒙平台
flutter·华为·harmonyos·flutterplugin·flutter鸿蒙化
雨季6666 小时前
Flutter 智慧物流仓储服务平台:跨端协同打造高效流转生态
flutter
勇气要爆发6 小时前
【第五阶段—高级特性和框架】第十一章:Flutter屏幕适配开发技巧—变形秘籍
flutter