【Flutter】显式动画

🔥 本文由 程序喵正在路上 原创,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,
        ),
      ),
    );
  }
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

k8s群集调度之 pod亲和 node亲和 标签指定

目录 一 调度约束 1.1K8S的 List-Watch 机制 ⭐⭐⭐⭐⭐ 1.1.1Pod 启动典型创建过程 二、调度过程 2.1Predicate&#xff08;预选策略&#xff09; 常见的算法 2.2priorities&#xff08;优选策略&#xff09;常见的算法 三、k8s将pod调度到指定node的方法 3.1指…

FPGA中的乒乓操作

为什么不直接选用一个缓存更大的FIFO而选用乒乓操作为什么乒乓操作可以实现低速处理高速数据乒乓操作适用哪些场景 一、乒乓操作结构 首先先介绍一下乒乓操作的原理&#xff0c;其结构如下&#xff1a; 输入选择单元负责将数据送到数据缓冲模块&#xff0c;然后输出选择单元负…

网络工程师---第四十三天

1、网络地址转换请简述DNS服务器迭代查询与递归的区别&#xff1f; 2、请从技术方面简述RAIDO、RAID1、RAID3、 RAID5的特点&#xff1f; 3、请从层次结构、部署设备和功能配置方面描述层次化的网络结构&#xff1f; 4、请简述IPSECVPN和AH和ESP的区别&#xff1f; 5、请简述ID…

用友U8 Cloud linkntb.jsp SQL注入漏洞复现(CNVD-C-2023-708748)

0x01 产品简介 用友U8 Cloud是用友推出的新一代云ERP,主要聚焦成长型、创新型企业,提供企业级云ERP整体解决方案。 0x02 漏洞概述 用友U8 Cloud linkntb.jsp 接口处存在SQL注入漏洞,未授权的攻击者可通过此漏洞获取数据库权限,从而盗取用户数据,造成用户信息泄露。 0x…

vue组件的基本使用方法

组件 【1】组件是什么&#xff1f; 组件就是&#xff1a;扩展 HTML 元素&#xff0c;封装可重用的代码&#xff0c;目的是复用例如&#xff1a;有一个轮播图&#xff0c;可以在很多页面中使用&#xff0c;一个轮播有js&#xff0c;css&#xff0c;html组件把js&#xff0c;cs…

[java基础揉碎]文件IO流

目录 文件 什么是文件 文件流​编辑 常用的文件操作 创建文件方式一 创建文件方式二 创建文件方式三 tip:为什么new file 了还有执行createNewFile?new File的时候其实是在内存中创建了文件对象, 还没有在磁盘中, 当执行createNewFile的时候才是往磁盘中写入​编辑 …

Golang 创建第一个web项目(Gin + Gorm)

1. 写这篇博客的由来&#xff1a; 当你想使用最快的框架创建项目的时候是不是有点束手无策&#xff1f; 当你想配置数据库写 SQL 甚至不知道如何写&#xff0c;文件夹都不知道建在哪里&#xff1f; &#x1f604;因为Golang 目前并没有 JAVA 那种硬性规范&#xff0c;但是…

基于JCEF实现网页资源性能分析

文章目录 1、需求2、实现代码3、运行效果 1、需求 在使用chrome浏览器访问页面时&#xff0c;浏览器自带的开发者工具可以查看页面每个资源的资源大小和加载时间。 这个功能可以让我们直接知道接口或资源的耗时情况和大小情况&#xff0c;如果存在性能问题&#xff0c;可以进…

LES物流执行系统,在离散制造行业有那些作用和价值?

离散制造企业往往面临的是多品种、小批量的非标订单生产&#xff0c;传统推动式物流系统已经无法应对计划变化滞后&#xff0c;各车间、工序之间难以衔接等情况&#xff0c;特别是密集劳动力的电子行业&#xff0c;非标产品 SKU 种类繁多&#xff0c;物料配送复杂&#xff0c;对…

Chisel入门——在windows下vscode搭建|部署Scala2.13.3开发环境|用Chisel点亮FPGA小灯

文章目录 前言一、vscode搭建scala开发环境1.1 安装Scala官方插件1.2 创建hello_world.scala文件1.3 确认java的版本(博主使用的是1.8)1.4 下载Scala Windows版本的二进制文件1.5 配置环境变量1.6 交互模式测试一下1.7 vscode运行scala 二、windows安装sbt2.1 下载sbt2.2 设置环…

mac安装Redis

官网&#xff1a; https://redis.io中文网&#xff1a; Redis中文网 安装 brew install redis 查看版本 redis-server --version 开启关闭服务 方式一&#xff08;不推荐&#xff09; 这种方式不太建议&#xff0c;因为控制台不用输出相应的日志 开启服务 brew service…

Qt项目使用pato mqtt C

一,下载pato mqtt C 源码 git 地址:https://github.com/eclipse/paho.mqtt.c.git git 地址可能下载不下来,提供我的gitee地址 gitee地址:https://gitee.com/chaojidahuaidan2021/paho.mqtt.c.git 二,编译共享库 clone下来后,将项目导入到Qt工程中,此时这是一个cmke工程…

uniapp一些问题解决

1.按钮边框如何去除&#xff1f; 参考博主&#xff1a;微信小程序按钮去不掉边框_微信小程序button去掉边框-CSDN博客文章浏览阅读1k次。最近在学uni-app&#xff0c;顺便自己写个小程序。左上角放了个button&#xff0c;可边框怎么也去不掉…原来微信小程序的按钮要去掉边框要…

辅助科技照亮道路,携手共促盲文书写技能新飞跃

在这个科技日新月异的时代&#xff0c;创新的力量正以前所未有的方式融入我们的日常生活&#xff0c;特别是对于视觉障碍群体而言&#xff0c;技术的每一次进步都是通往更加独立生活的桥梁。今天&#xff0c;让我们聚焦于一款名为“蝙蝠避障”的辅助软件&#xff0c;它不仅为盲…

Python数据可视化(六)

实现事件处理效果 我们借助 matplotlib 可以实现事件处理效果&#xff0c;例如&#xff0c;单击关闭画布会出现画布被关闭的文本提 示&#xff0c;在画布上的图形界面任意位置单击可以获得放大后的此处图形界面等。下面&#xff0c;我们就挑选一些 典型的事件处理案例来讲解实现…

基于微信小程序实现的【二手物品交易平台】后端 JAVA Springboot (内附设计LW + PPT+ 源码+ 演示视频 下载)

项目名称 项目名称&#xff1a; 基于微信小程序的二手物品交易平台 项目技术栈 该项目采用了以下核心技术栈&#xff1a; 后端框架/库&#xff1a; Java, SSM框架数据库&#xff1a; MySQL前端技术&#xff1a; 微信小程序技术其他相关技术&#xff1a; HTML, MyEclipse开发…

C#--Mapster(高性能映射)用法

1.Nuget安装Mapster包引用 2.界面XAML部分 <Window x:Class"WpfApp35.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.m…

Ubuntu配置Git

安装git sudo apt install git 查看是否安装成功 git --version 配置git 用github上注册的用户名和邮箱地址&#xff0c;配置git git config --global user.name "username" git config --global user.email "usernameemail.com" 重启ubuntu查看…

Filebeat进阶指南:核心架构与功能组件的深度剖析

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《洞察之眼&#xff1a;ELK监控与可视化》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是ELK 2、FileBeat在ELK中的角色 二、Fil…

【已解决】使用StringUtils.hasLength参数输入空格仍然添加成功定价为负数仍然添加成功

Bug情景 今天在做功能测试时&#xff0c;发现使用使用StringUtils.hasLength&#xff08;&#xff09;方法以及定价为负数时&#xff0c;添加图书仍然成功 思考过程 0.1 当时在做参数检验时用了spring提供的StringUtils工具包&#xff0c;百度/大数据模型说&#xff1a; 0.2…