【Flutter】显式动画

news2024/10/5 12:50:54

🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月29日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾

目录

  • 简介
  • RotationTransition组件
  • FadeTransition组件
  • ScaleTransition组件
  • SlideTransition组件
  • AnimatedIcon组件

简介

常见的显式动画有 RotationTransitionFadeTransitionScaleTransitionSlideTransitionAnimatedIcon。在显示动画中开发者需要创建一个 AnimationController,通过 AnimationController 来控制动画的开始、暂停、重置、跳转、倒播等。

RotationTransition组件

RotationTransition 组件是 Flutter 中的一个动画组件,用于在子组件进行旋转动画。它可以根据指定的旋转角度来对子组件进行旋转,并且可以在动画执行过程中实时更新旋转角度以创建平滑的动画效果。

要使用 RotationTransition 组件,你需要提供一个 Animation<double> 对象作为其参数,该动画对象控制着旋转的角度。通常,你可以使用 Flutter 提供的动画控制器(如AnimationController)来创建一个动画对象,并在控制器的值范围内更新角度值。

以下是 RotationTransition 组件的基本属性:

  • turns: 一个 Animation<double> 对象,控制旋转的角度。可以通过 Tween<double> 来创建这个动画对象,也可以使用动画控制器(如AnimationController)。

  • alignment: 一个 AlignmentGeometry 对象,表示旋转的中心点位置,默认为 Alignment.center,即子组件的中心点。

  • child: 要进行旋转动画的子组件。

AnimationController 普通用法:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('RotationTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          RotationTransition(
            turns: _controller,
            child: const FlutterLogo(
              size: 100,
            ),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

在这里插入图片描述

lowerBound、upperBound

AnimationController 用于控制动画,它包含动画的启动 forward() 、停止 stop() 、反向播放 reverse() 等方法。 AnimationController 会在动画的每一帧,就会生成一个新的值。默认情况下, AnimationController 在给定的时间段内线性的生成从 0.01.0(默认区间)的数字 ,我们也可以通过 lowerboundupperBound 来修改 AnimationController 生成数字的区间。

将前面程序的 initState 方法修改一下:


void initState() {
  super.initState();
  _controller = AnimationController(
    vsync: this,
    /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
    直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
    确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
    duration: const Duration(seconds: 1),
    lowerBound: 3, //第三圈转到第六圈
    upperBound: 6,
  );

  _controller.addListener(() {
    print(_controller.value);
  });
}

设置了 lowerBoundupperBound 后,AnimationController 的值就会在它们这个区间中变化,我们可以看到运行后,_controller 就会在 36 这个区间变化:

在这里插入图片描述

FadeTransition组件

FadeTransition 组件是 Flutter 中的一个动画组件,用于在子组件进行透明度渐变动画。它可以根据指定的透明度值来对子组件进行渐变动画,并且可以在动画执行过程中实时更新透明度值以创建平滑的动画效果。

我们将前面程序中的 RotationTransition 换成 FadeTransition,再将 _controller 赋值给它的参数 opacity 即可:

FadeTransition(
  opacity: _controller,
  child: const FlutterLogo(
    size: 100,
  ),
),

记得将刚刚 lowerBound、upperBound 去掉,如果要设置 lowerBound、upperBound,不能大于1,因为透明度在大于 1 后都是一样的。

ScaleTransition组件

ScaleTransition 组件是 Flutter 中的一个动画组件,用于在子组件进行缩放动画。它可以根据指定的缩放比例来对子组件进行缩放动画,并且可以在动画执行过程中实时更新缩放比例以创建平滑的动画效果。

AnimationController 控制动画:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ScaleTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ScaleTransition(
            scale: _controller,
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

AnimationController 结合 Tween 控制动画:

默认情况下, AnimationController 对象值的范围是 [0.0,1.0]。如果我们需要构建 UI 的动画值在不同的范围或不同的数据类型,则可以使用 Tween 来添加映射以生成不同的范围或数据类型的值。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('ScaleTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ScaleTransition(
            scale: _controller.drive(Tween(begin: 1, end: 2)),
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

SlideTransition组件

这是一个负责平移的显示动画组件,使用时需要通过 position 属性传入一个 Animated 表示位移程度,通常借助 Tween 实现。

_controller.drive驱动动画:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SlideTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(
            position: _controller.drive(
              Tween(
                begin: const Offset(0, 0),
                end: const Offset(1.2, 0), //表示实际的位置为向右移动自身宽度的1.2倍
              ),
            ),
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

Tween.animate 驱动动画:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SlideTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(
            position: Tween(
              begin: const Offset(0, 0),
              end: const Offset(1.2, 0), //表示实际的位置为向右移动自身宽度的1.2倍
            ).animate(_controller),
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

链式操作修改动画效果:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SlideTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(
            position: Tween(
              begin: const Offset(0, -1),
              end: const Offset(0, 3),
            ).chain(CurveTween(curve: Curves.bounceInOut)).animate(_controller),
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

链式操作修改动动画执行时间:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      /*Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂
      直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,
      确保刷新率和帧率保持同步,以达到供需平衡的效果,防止卡顿现象。 */
      duration: const Duration(seconds: 1),
    );

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SlideTransition'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(
            position: Tween(
              begin: const Offset(0, -1),
              end: const Offset(0, 3),
            )
                .chain(CurveTween(curve: Curves.bounceInOut))
                .chain(
                    CurveTween(curve: const Interval(0.8, 1))) //在最后20%的时间内完成动画
                .animate(_controller),
            child: const FlutterLogo(size: 100),
          ),
          const SizedBox(height: 40),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Wrap(
              spacing: 10,
              alignment: WrapAlignment.center,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _controller.forward(); //正序播放一次
                  },
                  child: const Text("Forward"),
                ),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
                ElevatedButton(
                    onPressed: () {
                      _controller.stop(); //停止播放
                    },
                    child: const Text("Stop")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reset(); //重置
                    },
                    child: const Text("rest")),
                ElevatedButton(
                    onPressed: () {
                      _controller.repeat(); //重复播放
                    },
                    child: const Text("repeat"))
              ],
            ),
          ),
        ],
      ),
    );
  }
}

AnimatedIcon组件

AnimatedIcon 顾名思义,是一个用于提供动画图标的组件,它的名字虽然是以 Animated 开头,但是他是一个显式动画组件,需要通过 progress 属性传入动画控制器,另外需要由 Icon 属性传入动画图标数据。

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

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

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));

    _controller.addListener(() {
      print(_controller.value);
    });
  }

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

  void _toggleAnimation() {
    if (_controller.status == AnimationStatus.completed) {
      _controller.reverse();
    } else {
      _controller.forward();
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: _toggleAnimation,
        child: const Icon(Icons.refresh),
      ),
      appBar: AppBar(
        title: const Text('AnimatedIcon'),
      ),
      body: Center(
        child: AnimatedIcon(
          icon: AnimatedIcons.close_menu,
          progress: _controller,
          size: 40,
        ),
      ),
    );
  }
}

点击浮动按钮后,图标会在关闭和菜单之间变换。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1711714.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ORA-12514:TNS:监听程序当前无法识别连接描述符中请求的服务

ORA-12514&#xff1a;TNS&#xff1a;监听程序当前无法识别连接描述符中请求的服务 问题描述&#xff1a; 解决方案&#xff1a; 1、检查oracle的监听服务是否运行正常 1)点击键盘的winr&#xff0c;输入services.msc&#xff0c;点击确认/回车键&#xff1b; 2&#xff09;查…

2023年信息素养大赛小学组C++智能算法复赛真题

今天给大家分享2023年全国青少年信息素养大赛小学组C智能算法挑战赛复赛里面的一套真题&#xff0c;希望有助于大家了解复赛的难度及备考。 其他真题下载&#xff1a;网盘-真题-信息素养大赛

PyTorch安装与配置

前言 参考文档&#xff1a;https://github.com/TingsongYu/PyTorch-Tutorial-2nd 环境配置之Anaconda 解释器——python.exe&#xff0c;是人类与CPU之间的桥梁&#xff0c;需要配置系统环境变量 Anaconda&#xff1a;集成环境&#xff0c;包管理器 Conda 安装 Anaconda&am…

12.2 通道-阻塞与流程控制、通道型函数、退出通道

阻塞与流程控制 通常在并发程序中要尽力避免阻塞式操作&#xff0c;但有时又需要让代码暂时处于阻塞状态&#xff0c;以等待某种条件、信号或数据&#xff0c;然后再继续运行。 对于无缓冲通道&#xff0c;试图从无人写入的通道中读取&#xff0c;或者向无人读取的通道中写入…

学习笔记——数据通信基础——数据通信网络(拓扑结构)

网络拓扑 网络拓扑(Network Topology)是指用传输介质(例如双绞线、光纤等)互连各种设备(例如计算机终端、路由器、交换机等)所呈现的结构化布局。 1、网络拓扑形态 星型网络∶所有节点通过一个中心节点连接在一起。 优点∶容易在网络中增加新的节点。通信数据必须经过中心节点…

学习Uni-app开发小程序Day21

学习了评分组件、自定义导航栏 评分组件uni-rate 这是需要达到的效果图&#xff0c;这里先分析下效果图&#xff0c; 1、图片是从布局中间弹出的&#xff0c;那这里就要用到uni-popup &#xff0c;设置type从中间弹出 2、这个弹出的顶部和上一张的顶部布局是一样的&#xff0c…

C# 反射GetProperties和GetFields的坑

有时候使用反射&#xff0c;获取类的所有字段和所有属性&#xff0c;一般情况下是按照我们写的先后顺序返回的。 但是我今天碰到了一次不是按照顺序返回的&#xff01;&#xff01;&#xff01; 翻看文档&#xff1a; GetProperties&#xff1a; https://learn.microsoft.com/…

【C++】从零开始构建红黑树 —— 节点设计,插入函数的处理 ,旋转的设计

送给大家一句话&#xff1a; 日子没劲&#xff0c;就过得特别慢&#xff0c;但凡有那么一点劲&#xff0c;就哗哗的跟瀑布似的拦不住。 – 巫哲 《撒野》 &#x1f30b;&#x1f30b;&#x1f30b;&#x1f30b;&#x1f30b;&#x1f30b;&#x1f30b;&#x1f30b; ⛰️⛰️…

WordPress建网站公司 建易WordPress建站

建易WordPress建网站公司是一家专业从事WordPress网站建设、网站维护、网站托管、运营推广和搜索引擎优化(SEO)等服务的公司。建易WordPress建网站公司提供多种服务&#xff0c;包括模板建站和定制网站&#xff0c;并且明码标价&#xff0c;价格透明&#xff0c;竭诚为全国各地…

常见5大开发进度盲点问题及解决方案

在软件开发项目中&#xff0c;识别并解决常见的进度管理盲点问题&#xff0c;对于确保项目按时、按预算、高质量完成至关重要。它直接关系到项目能否顺利进行&#xff0c;忽视任何一个问题&#xff0c;都可能导致项目延期、成本超支、质量下降&#xff0c;甚至项目失败。 因此&…

G60-M60F-ZQ手动抓取快速接头,专用于吊装设备的重物快速抓取

客户需求概述&#xff1a; 客户需要将重达将近400公斤的产品从一个工作台移动至另一个工作台&#xff0c;目前的方法是通过人工将吊环的螺纹与产品的螺纹相互拧紧&#xff0c;然后利用装备吊起移动&#xff0c;但这种方式效率低下&#xff0c;且因为工人的操作有时难以达到理想…

CHIMA专访美创高级总监丁斐:为医疗数据安全构筑体系化防御新机制

5月17-19日&#xff0c;中国医院信息网络大会&#xff08;CHIMA 2024&#xff09;在南京隆重召开。作为结识多年的老友&#xff0c;美创科技再携以数据为中心的全系列安全业务、新一代数字化安全平台、医疗行业解决方案精彩亮相。 会议期间&#xff0c;CHIMA专访美创科技&…

Linux之sshpass命令

介绍 sshpass是一个工具&#xff0c;用于通过SSH连接到远程服务器时自动输入密码。它允许您在命令行中指定密码&#xff0c;以便在建立SSH连接时自动进行身份验证。 安装 # 以centos为例 yum install sshpass -y 使用方法 sshpass [-f filename | -d num | -p password | …

精酿啤酒:品质与口感在啤酒品牌形象建设中的作用

啤酒品牌形象建设是提升市场竞争力的关键&#xff0c;而品质与口感在其中扮演着重要的角色。对于Fendi club啤酒而言&#xff0c;其卓着的品质和与众不同的口感在品牌形象建设中发挥了积极的作用。 品质是啤酒品牌形象的核心要素。消费者对啤酒品质的要求越来越高&#xff0c;品…

新书推荐:7.5 goto、break、continue语句

本节必须掌握的知识点&#xff1a; 示例二十六 代码分析 汇编解析 示例二十七 代码分析 汇编解析 7.5.1 示例二十六 ■goto语句&#xff1a;无条件转移语句。 语法格式&#xff1a; goto label; label : 代码; ●语法解析&#xff1a; 执行到goto语句时&#xff0c;则无…

【PB案例学习笔记】-10 进度条使用

写在前面 这是PB案例学习笔记系列文章的第10篇&#xff0c;该系列文章适合具有一定PB基础的读者。 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gite…

【python】OpenCV—Tracking(10.2)

文章目录 BackgroundSubtractorcreateBackgroundSubtractorMOG2createBackgroundSubtractorKNN BackgroundSubtractor Opencv 有三种背景分割器 K-Nearest&#xff1a;KNN Mixture of Gaussian&#xff08;MOG2&#xff09; Geometric Multigid&#xff08;GMG&#xff09; …

酒店提前线上订房小程序源码系统 PHP+MySQL组合开发 源码开源可二开 带完整的安装代码包以及搭建教程

系统概述 随着移动互联网的普及&#xff0c;越来越多的人习惯通过手机进行酒店预订。传统的线下订房方式逐渐无法满足用户的需求&#xff0c;酒店提前线上订房小程序的出现成为必然趋势。该源码系统的开发旨在为酒店提供一个便捷、高效的线上订房平台&#xff0c;提升用户体验…

【Java】【python】leetcode刷题记录--双指针

双指针也一般称为快慢指针&#xff0c;主要用于处理链表和数组等线性数据结构。这种技巧主要涉及到两个指针&#xff0c;一个快指针&#xff08;通常每次移动两步&#xff09;和一个慢指针&#xff08;通常每次移动一步&#xff09;。快指针可以起到’探路‘的作用&#xff0c;…

2-EMMC启动及各分区文件生成过程

EMMC的使用比nand flash还是复杂一些&#xff0c;有其特有的分区和电器性能 1、启动过程介绍 跟普通nand或spi flash不同&#xff0c;uboot前面还有好几级 在vendor某些厂商的设计中&#xff0c;ATF并不是BOOTROM加载后的第一个启动镜像&#xff0c;可能是这样的&#xff1a; …