Flutter动画 显示动画 隐式动画

发布于 1周前 作者 phonegap100 最后一次编辑是 5天前 来自 分享

Flutter动画 显示动画 隐式动画对应视频教程地址https://www.bilibili.com/video/BV1S4411E7LY?p=47

一、 动画基本原理以及Flutter动画简介

1、 动画原理

在任何系统的UI框架中,动画实现的原理都是相同的,即:在一段时间内,快速地多次改变UI外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画,这和电影的原理是一样的。我们将UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数。很明显,帧率越高则动画就会越流畅!一般情况下,对于人眼来说,动画帧率超过16 FPS,就基本能看了,超过 32 FPS就会感觉相对平滑,而超过 32 FPS,大多数人基本上就感受不到差别了。由于动画的每一帧都是要改变UI输出,所以在一个时间段内连续的改变UI输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在UI系统中,动画的平均帧率是重要的性能指标,而在Flutter中,理想情况下是可以实现 60FPS 的,这和原生应用能达到的帧率是基本是持平的。

2、 Flutter动画简介

FLutter中的动画主要分为:隐式动画、显式动画、自定义隐式动画、自定义显式动画、和 Hero 动画 。

二、Flutter隐式动画

通过几行代码就可以实现隐式动画,由于隐式动画背后的实现原理和繁琐的操作细节都被隐去了,所以叫隐式动画,FLutter中提供的 AnimatedContainer、AnimatedPadding、AnimatedPositioned、AnimatedOpacity、AnimatedDefaultTextStyle、AnimatedSwitcher都属于隐式动画。

隐式动画中可以通过duration配置动画时长、可以通过Curve(曲线)来配置动画过程

2.1、AnimatedContainer

AnimatedContainer的属性和Container属性基本是一样的,当AnimatedContainer属性改变的时候就会触发动画。

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 
  bool flag=true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.animation),
        onPressed: () {
          setState(() {
            flag=!flag;
          });
        },
      ),
      appBar: AppBar(
        title: const Text("AnimatedContainer Demo"),
      ),
      body: Center(
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 500), // 动画时长500 ms
          width: flag?100:300,
          height:flag?100:300,
          color: Colors.blue,
        ),
      ),
    );
  }
}

2.2、AnimatedPadding以及curve属性

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.animation),
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
      ),
      appBar: AppBar(
        title: const Text("AnimatedContainer Demo"),
      ),
      body: AnimatedPadding(
        duration: const Duration(milliseconds: 2000), // 动画时长500 ms
        curve: Curves.bounceInOut,
        padding: EdgeInsets.fromLTRB(10, flag ? 10 : 500, 0, 0),

        child: Container(
          width: 100,
          height: 100,
          color: Colors.red,
        ),
      ),
    );
  }
}

Curves曲线值

曲线名 动画过程
linear 匀速的
decelerate 匀减速
ease 开始加速,后面减速
easeIn 开始慢,后面快
easeOut 开始快,后面慢
easeInOut 开始慢,然后加速,最后再减速
更多曲线 https://docs.flutter.io/flutter/animation/Curves-class.html 官方文档打不开也可以参考教程目录中提供的gif截图

2.3、AnimatedPositioned

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Stack(
        children: [
          AnimatedPositioned(
            curve: Curves.easeInOut,
            duration: const Duration(seconds: 1),
            top: flag ? 10 : 500,
            left: flag ? 10 : 300,
            child: Container(
              width: 60,
              height: 60,
              color: Colors.blue,
            ),
          ),
          Align(
            alignment: const Alignment(0, 0.8),
            child: ElevatedButton(
              child: const Text("Transform"),
              onPressed: () {
                setState(() {
                  flag = !flag;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

2.4、AnimatedOpacity

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          setState(() {
            flag=!flag;
          });
        },
        child: const Icon(Icons.opacity),
      ),
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Center(
        child: AnimatedOpacity(
            curve: Curves.linear,
            duration: const Duration(seconds: 1),
            opacity: flag?1:0,           
            child: Container(
              width: 300,
              height: 300,
              color: Colors.blue,
            ),
          ),
      ),
    );
  }
}

2.5、AnimatedDefaultTextStyle

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
        child: const Icon(Icons.opacity),
      ),
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Center(
        child: Container(
          alignment: Alignment.center,
          width: 300,
          height: 300,
          color: Colors.blue,
          child:  AnimatedDefaultTextStyle(
            duration: const Duration(seconds: 1),
            style: TextStyle(
              fontSize: flag?20:50,
            ),
            child: const Text("你好Flutte"),
          ),
        ),
      ),
    );
  }
}

2.6、AnimatedSwitcher 以及transitionBuilder

上面讲的AnimatedContainer、AnimatedPadding、AnimatedPositioned、AnimatedOpacity、AnimatedDefaultTextStyle都是在属性改变的时候执行动画,AnimatedSwitcher则是在子元素改变的时候执行动画。

相比上面的动画组件AnimatedSwitcher多了transitionBuilder参数,可以在transitionBuilder中自定义动画

**示例1:**载页面前显示一个loading动画,加载完毕后显示对于的内容

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
        child: const Icon(Icons.opacity),
      ),
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Center(
        child: Container(
          alignment: Alignment.center,
          width: 300,
          height: 180,
          color: Colors.yellow,
          child: AnimatedSwitcher(
            duration: const Duration(milliseconds: 1000),
            child: flag
              ? const CircularProgressIndicator()
              : Image.network(
                  "https://www.itying.com/images/flutter/2.png",
                  fit: BoxFit.contain,
                ),
          ),
        ),
      ),
    );
  }
}

**示例2:**通过transitionBuilder自定义动画效果

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
        child: const Icon(Icons.opacity),
      ),
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Center(
        child: Container(
          alignment: Alignment.center,
          width: 300,
          height: 180,
          color: Colors.yellow,
          child: AnimatedSwitcher(
            transitionBuilder: ((child, animation) {
              return  ScaleTransition(               
                scale: animation,
                child: FadeTransition(
                  opacity: animation,
                  child:child ,
                ),
              );
            }),
            duration: const Duration(milliseconds: 400),
            child: flag
              ? const CircularProgressIndicator()
              : Image.network(
                  "https://www.itying.com/images/flutter/2.png",
                  fit: BoxFit.contain,
                ),
          ),
        ),
      ),
    );
  }
}

**示例3:**通过transitionBuilder改变子元素执行动画

import 'dart:isolate';
import 'dart:math';

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

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

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
        child: const Icon(Icons.opacity),
      ),
      appBar: AppBar(
        title: const Text("AnimatedPositioned Demo"),
      ),
      body: Center(
        child: Container(
          alignment: Alignment.center,
          width: 300,
          height: 180,
          color: Colors.yellow,
          child: AnimatedSwitcher(
            transitionBuilder: ((child, animation) {
              return  ScaleTransition(               
                scale: animation,
                child: FadeTransition(
                  opacity: animation,
                  child:child ,
                ),
              );
            }),
            duration: const Duration(milliseconds: 400),
            child: Text(key:UniqueKey(),flag?"你好Flutter":"你好大地",style: const TextStyle(fontSize: 30),),
          ),
        ),
      ),
    );
  }
}

三 、Flutter显式动画

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

3.1、RotationTransition、 AnimationController

**示例1:**AnimationController普通用法
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      
      appBar: AppBar(
        title: const Text('Title'),
      ),
      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"))
              ],
            ),)
        ],
      ),
    );
  }
}

**示例2:**lowerBound upperBound

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

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      
      appBar: AppBar(
        title: const Text('Title'),
      ),
      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"))
              ],
            ),)
        ],
      ),
    );
  }
}

3.2、FadeTransition

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          FadeTransition(
            opacity: _controller,
            child: const FlutterLogo(size: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

3.3、ScaleTransition、Tween

**示例1:**AnimationController控制动画
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ScaleTransition(            
            scale: _controller,
            child: const FlutterLogo(size: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),                
              ],
            ),
          )
        ],
      ),
    );
  }
}

示例2: 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({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          ScaleTransition(            
            scale: _controller.drive(Tween(begin: 1,end: 2)),
            child: const FlutterLogo(size: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),                
              ],
            ),
          )
        ],
      ),
    );
  }
}

3.4、SlideTransition

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

示例1: _controller.drive驱动动画
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      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: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

示例2:Tween.animate 驱动动画
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      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: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

示例3: 链式操作修改动画效果
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(            
            position: Tween(
              begin: const Offset(0,-1),
              end: const Offset(0,0.8)  //表示实际的位置向右移动自身宽度的1.2倍
            ).chain(CurveTween(curve: Curves.bounceIn)).animate(_controller),
            child: const FlutterLogo(size: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

示例4: 链式操作修改动动画执行时间
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    // TODO: implement initState

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          SlideTransition(            
            position: Tween(
              begin: const Offset(0,-1),
              end: const Offset(0,0.8)  //表示实际的位置向右移动自身宽度的1.2倍
            ).chain(CurveTween(curve: Curves.bounceIn))
            .chain(CurveTween(curve: const Interval(0.8,1.0)))  //最后的百分之20的时间完成动画
            .animate(_controller),
            child: const FlutterLogo(size: 80),
          ),
          const SizedBox(
            height: 40,
          ),
          Padding(
            padding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                    onPressed: () {
                      _controller.forward(); //正序播放一次
                    },
                    child: const Text("Forward")),
                ElevatedButton(
                    onPressed: () {
                      _controller.reverse(); //倒序播放一次
                    },
                    child: const Text("Reverse")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

3.5、AnimatedIcon

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

202208108102537

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));
  }
  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.add),
        onPressed: () {
          _controller.forward();
        },
      ),
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child:
            AnimatedIcon(icon: AnimatedIcons.menu_close,progress: _controller,size:40),
      ),
    );
  }
}

四 、交错动画

jiaocuo

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.refresh),
        onPressed: () {
          flag ? _controller.forward() : _controller.reverse();
          flag = !flag;
        },
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            SlidingBox(
              controller: _controller,
              color: Colors.blue[200],
              curve: const Interval(0, 0.2),
            ),
            SlidingBox(
              controller: _controller,
              color: Colors.blue[400],
              curve: const Interval(0.2, 0.4),
            ),
            SlidingBox(
              controller: _controller,
              color: Colors.blue[600],
              curve: const Interval(0.4, 0.6),
            ),
            SlidingBox(
              controller: _controller,
              color: Colors.blue[800],
              curve: const Interval(0.6, 0.8),
            ),
            SlidingBox(
              controller: _controller,
              color: Colors.blue[900],
              curve: const Interval(0.8, 1.0),
            ),
          ],
        ),
      ),
    );
  }
}

class SlidingBox extends StatelessWidget {
  final AnimationController controller;
  final Color? color;
  final Curve curve;
  const SlidingBox(
      {super.key,
      required this.controller,
      required this.color,
      required this.curve});

  @override
  Widget build(BuildContext context) {
    return SlideTransition(
      position: Tween(begin: const Offset(0, 0), end: const Offset(0.3, 0))
          .chain(CurveTween(curve: Curves.bounceInOut))
          .chain(CurveTween(curve: curve))
          .animate(controller),
      child: Container(
        width: 220,
        height: 60,
        color: color,
      ),
    );
  }
}

五 、自定义动画

5.1 、TweenAnimationBuilder自定义隐式动画

每当Tween的end发生变化的时候就会触发动画。

示例1:大小变化
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.refresh),
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
      ),
      body: Center(
        child: TweenAnimationBuilder(
            tween: Tween(begin: 100.0, end: flag ? 100.0 : 200.0),
            duration: const Duration(seconds: 1),
            builder: ((context, value, child) {
              return Icon(
                Icons.star,
                size: value as double,
              );
            })),
      ),
    );
  }
}
示例2:透明度变化
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool flag = true;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      floatingActionButton: FloatingActionButton(
        child: const Icon(Icons.refresh),
        onPressed: () {
          setState(() {
            flag = !flag;
          });
        },
      ),
      body: Center(
        child: TweenAnimationBuilder(
            tween: Tween(begin: 0.0, end: flag ? 0.2 : 1.0),
            duration: const Duration(seconds: 1),
            builder: ((context, value, child) {
              return Opacity(opacity: value as double,child: Container(
                color: Colors.red,
                width: 200,
                height: 200,
              ),) ;
            })),
      ),
    );
  }
}

5.2 、AnimatedBuilder 自定义显式动画

示例1:透明度动画
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1))
          ..repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Opacity(
              opacity: _controller.value, //从0到1的变化
              child: Container(
                width: 200,
                height: 200,
                color: Colors.red,
                child: const Text("我是一个text组件"),
              ),
            );
          },
        ),
      ),
    );
  }
}

示例2:自定义变化范围
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1))
          ..repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Opacity(
              opacity: Tween(begin: 0.5,end: 1.0).animate(_controller).value, //从0.5到1的变化
              child: Container(
                width: 200,
                height: 200,
                color: Colors.red,
                child: const Text("我是一个text组件"),
              ),
            );
          },
        ),
      ),
    );
  }
}

示例3:位置变化
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1))
          ..repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    Animation x = Tween(begin: -100.0, end: 100.0)
        .chain(CurveTween(curve: Curves.easeIn))
        .chain(CurveTween(curve: const Interval(0.2, 0.8)))       
        .animate(_controller);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Container(
              width: 200,
              height: 200,
              color: Colors.red,
              transform: Matrix4.translationValues(x.value, 0, 0),
              child: const Text("我是一个text组件"),
            );
          },
        ),
      ),
    );
  }
}

示例4:chilid优化
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  @override
  void initState() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1))
          ..repeat(reverse: true);
  }

  @override
  Widget build(BuildContext context) {
    Animation x = Tween(begin: -100.0, end: 100.0)
        .chain(CurveTween(curve: Curves.easeIn))
        .chain(CurveTween(curve: const Interval(0.2, 0.4)))
        .animate(_controller);
    return Scaffold(
      appBar: AppBar(
        title: const Text('Title'),
      ),
      body: Center(
        child: AnimatedBuilder(
          animation: _controller,
          builder: (BuildContext context, Widget? child) {
            return Container(
              width: 200,
              height: 200,
              color: Colors.red,
              transform: Matrix4.translationValues(x.value, 0, 0),
              child: child,
            );
          },
          child: const Text("我是一个text组件"),
        ),
      ),
    );
  }
}

回到顶部