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

参考

相关推荐
程序员老刘2 小时前
谨慎升级macOS 15.4,规避 ITMS-90048 错误
flutter·macos·ios
关山月4 小时前
📄Flutter & Nuvigator:路由器和参数的介绍
flutter
leluckys4 小时前
flutter 专题 一百 Flutter技术全解析
flutter
getapi5 小时前
flutter点击事件教程
flutter
pengyu7 小时前
系统化掌握Dart网络编程之Dio(四):拦截器篇
android·flutter·dart
bst@微胖子8 小时前
Flutter之用户输入&网络数据&缓存
android·flutter·缓存
恋猫de小郭12 小时前
注意,暂时不要升级 MacOS ,Flutter/RN 等构建 ipa 可能会因 「ITMS-90048」This bundle is invalid 被拒绝
android·前端·flutter
前端极客探险家15 小时前
Flutter vs React Native:跨平台移动开发框架对比
flutter·react native·react.js
每次的天空21 小时前
Flutter学习总结之Android渲染对比
android·学习·flutter
LinXunFeng1 天前
Flutter - Xcode16 还原编译速度
前端·flutter·xcode