Flutter 06 动画

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

1、动画原理:

在任何系统的Ul框架中,动画实现的原理都是相同的,即:在一段时间内,快速地多次改变Ul外观;由于人眼会产生视觉暂留,所以最终看到的就是一个“连续”的动画,这和电影的原理是一样的。

我们将UI的一次改变称为一个动画帧,对应一次屏幕刷新,而决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数。

很明显,帧率越高则动画就会越流畅!一般情况下,对于人眼来说,动画帧率超过16FPS,就基本能看了,超过32FPS就会感觉相对平滑,而超过32FPS,大多数人基本上就感受不到差别了。

由于动画的每一帧都是要改变Ul输出,所以在一个时间段内连续的改变Ul输出是比较耗资源的,对设备的软硬件系统要求都较高,所以在Ul系统中,动画的平均帧率是重要的性能指标,而在Flutter中,理想情况下是可以实现60FPS的,这和原生应用能达到的帧率是基本是持平的。

2、FLutter中的动画主要分为:

隐式动画、显式动画、自定义隐式动画、自定义显式动画、Hero动画;

二、隐式动画:

1、说明:

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

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

2、Curves曲线值:

linear 匀速的;
decelerate 匀减速;
ease 开始加速,后面减速;
easeln 开始慢,后面快;
easeOut 开始快,后面慢;
easelnOut 开始慢,然后加速,最后再减速;

3、AnimatedContainer:

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

代码示例:放大缩小动画

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> {
  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,

        ),
      ),
    );
  }
}

4、AnimatedPadding:

代码示例:上下移动动画
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> {
  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,
        ),
      ),
    );
  }
}

5、AnimatedPositioned:

代码示例:对角线移动动画

import 'package:flutter/material.dart';

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

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

  @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;
                });
              },
            ),
          ),
        ],
      ),
    );
  }
}

6、AnimatedOpacity:

代码示例:淡入淡出动画

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

import 'package:flutter/material.dart';

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

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

  @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,
          ),
        ),
      ),
    );
  }
}

7、AnimatedDefaultTextStyle:

代码示例:字体放大缩小动画

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

import 'package:flutter/material.dart';

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

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

  @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"),
          ),
        ),
      ),
    );
  }
}

8、AnimatedSwitcher 和 transitionBuilder:

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

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

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

import 'package:flutter/material.dart';

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

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

  @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://pics2.baidu.com/feed/b7003af33a87e9502b64d86f4c2e9544fbf2b45b.jpeg@f_auto?token=8c6557279177a75d44029840f0db0daa&s=C8AA67D91C0090457095310903005057",
                    fit: BoxFit.contain,
                  ),
          ),
        ),
      ),
    );
  }
}
代码示例2、通过transitionBuilder自定义动画效果:

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

import 'package:flutter/material.dart';

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

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

  @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://pics2.baidu.com/feed/b7003af33a87e9502b64d86f4c2e9544fbf2b45b.jpeg@f_auto?token=8c6557279177a75d44029840f0db0daa&s=C8AA67D91C0090457095310903005057",
                    fit: BoxFit.contain,
                  ),
          ),
        ),
      ),
    );
  }
}
代码示例3、通过transitionBuilder改变子元素执行动画:

import 'package:flutter/material.dart';

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

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

  @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),),
          ),
        ),
      ),
    );
  }
}

三、显式动画:

1、说明:

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

2、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() {
    super.initState();
    // TODO: implement initState
    _controller = AnimationController(
      // Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂直同步信号,
      // 只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保
      // 持同步,以达到供需平衡的效果,防止卡顿现象。
      vsync:
      this,
      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)IowerBound 和 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,
        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、FadeTransition 淡入淡出

代码示例:

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 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() {

    _controller = AnimationController(
      vsync: this,
      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: [
          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")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

4、ScaleTransition缩放 和 Tween

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

代码示例1、AnimationController控制动画:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}
//示例1:AnimationController控制动画
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @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,
      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控制动画:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}
//示例2:AnimationController结合Tween控制动画
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @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,
      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")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

5、SlideTransition平移

这是一负责平移的显示动画组件,使用时需要通过position属性传入一个Animated表示位移程度,通常借助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() {
    _controller = AnimationController(
      vsync: this,
      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),
                  ),
              ),
            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() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      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,
      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))
                    .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,
      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))
                    .chain(CurveTween(curve: Curves.bounceIn))
                    .chain(CurveTween(curve: const Interval(0.8, 1.0)))
                    .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")),
              ],
            ),
          )
        ],
      ),
    );
  }
}

四、Animatedlcon:

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

代码示例:旋转改变图标

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() {
    super.initState();
    _controller =
        AnimationController(vsync: this, duration: const Duration(seconds: 1));
  }

  @override
  void 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(
            //图标效果列表:https://api.flutter.dev/flutter/material/AnimatedIcons-class.html
            icon: AnimatedIcons.menu_close, progress: _controller, size: 40),
      ),
    );
  }
}

五、交错动画:

代码示例:

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() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      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,
      ),
    );
  }
}

六、自定义动画:

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,
              );
            })),
      ),
    );
  }
}
代码示例2、透明度变化:

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 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.0 : 1.0),
            duration: const Duration(seconds: 1),
            builder: ((context, value, child) {
              return Opacity(
                opacity: value,
                child: Container(
                  color: Colors.red,
                  width: 200,
                  height: 200,
                ),
              );
            })),
      ),
    );
  }
}

2、AnimatedBuilder 自定义显式动画

代码示例1、透明度动画:

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 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、child优化:

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组件"),
        ),
      ),
    );
  }
}

七、Hero 动画:

1、Hero动画的使用:

微信朋友圈点击小图片的时候会有一个动画效果到大图预览,这个动画效果就可以使用Hero动画实现。

Hero 指的是可以在路由(页面)之间“飞行”的widget,简单来说 Hero 动画就是在路由切换时,有一个共享的widget 可以在新旧路由间切换。

代码示例:

home.dart:
import 'package:flutter/material.dart';
import '../../res/listData.dart';
import 'hero.dart';
import 'hero2.dart';

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

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

class _HomePageState extends State<HomePage> {
  List<Widget> _getListData() {
    var tempList = listData2.map((value) {
      return GestureDetector(
        onTap: () {
          Navigator.push(context, MaterialPageRoute(builder: (setting) {
            // return HeroPage(arguments: {
            //   "imageUrl": value['imageUrl'],
            //   "description": value['title'],
            // });
            return HeroPage(arguments: {
              "imageUrl": value['imageUrl'],
              "description": value['title'],
            });
          }));
        },
        child: Container(
            decoration: BoxDecoration(
                border: Border.all(
                    color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
            child: Column(
              children: <Widget>[
                Hero(
                  tag: value['title'],
                  // child: Image.asset(value['imageUrl'])),
                  child: Image.network(
                    value['imageUrl'],
                    width: 320,
                    height: 120,
                    fit: BoxFit.cover,
                  ),
                ),
                const SizedBox(height: 12),
                Text(
                  value['title'],
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontSize: 20),
                )
              ],
            )),
      );
    });
    return tempList.toList();
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisSpacing: 10.0,
      //水平子 Widget 之间间距
      mainAxisSpacing: 10.0,
      //垂直子 Widget 之间间距
      padding: const EdgeInsets.all(10),
      crossAxisCount: 2,
      //一行的 Widget 数量
      // childAspectRatio:0.7,
      // 宽度和高度的比例
      children: _getListData(),
    );
  }
}
hero.dart:
import 'package:flutter/material.dart';

class HeroPage extends StatefulWidget {
  final Map arguments;

  const HeroPage({super.key, required this.arguments});

  @override
  State<HeroPage> createState() => _HeroPageState();
}

class _HeroPageState extends State<HeroPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("详情页面"),
      ),
      body: ListView(
        children: [
          Hero(
              tag: widget.arguments["imageUrl"],
              child: Image.network(widget.arguments["imageUrl"])),
          const SizedBox(height: 20),
          Padding(
            padding: const EdgeInsets.all(5),
            child: Text(widget.arguments["description"],
                style: const TextStyle(fontSize: 22)),
          )
        ],
      ),
    );
  }
}

2、Hero +photo_view 实现微信朋友圈图片预览:

photo_view预览图片: 

 

代码示例:

home.dart:
import 'package:flutter/material.dart';
import '../../res/listData.dart';
import 'hero.dart';
import 'hero2.dart';

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

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

class _HomePageState extends State<HomePage> {
  List<Widget> _getListData() {
    var tempList = listData2.map((value) {
      return GestureDetector(
        onTap: () {
          Navigator.push(context, MaterialPageRoute(builder: (setting) {
            // return HeroPage(arguments: {
            //   "imageUrl": value['imageUrl'],
            //   "description": value['title'],
            // });
            return HeroPage2(arguments: {
              "imageUrl": value['imageUrl'],
              "description": value['title'],
            });
          }));
        },
        child: Container(
            decoration: BoxDecoration(
                border: Border.all(
                    color: const Color.fromRGBO(233, 233, 233, 0.9), width: 1)),
            child: Column(
              children: <Widget>[
                Hero(
                  tag: value['title'],
                  // child: Image.asset(value['imageUrl'])),
                  child: Image.network(
                    value['imageUrl'],
                    width: 320,
                    height: 120,
                    fit: BoxFit.cover,
                  ),
                ),
                const SizedBox(height: 12),
                Text(
                  value['title'],
                  textAlign: TextAlign.center,
                  style: const TextStyle(fontSize: 20),
                )
              ],
            )),
      );
    });
    return tempList.toList();
  }

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      crossAxisSpacing: 10.0,
      //水平子 Widget 之间间距
      mainAxisSpacing: 10.0,
      //垂直子 Widget 之间间距
      padding: const EdgeInsets.all(10),
      crossAxisCount: 2,
      //一行的 Widget 数量
      // childAspectRatio:0.7,
      // 宽度和高度的比例
      children: _getListData(),
    );
  }
}
hero2.dart:
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';

class HeroPage2 extends StatefulWidget {
  final Map arguments;
  const HeroPage2({super.key, required this.arguments});

  @override
  State<HeroPage2> createState() => _HeroPageState();
}

class _HeroPageState extends State<HeroPage2> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        Navigator.pop(context);
      },
      child: Hero(
        tag: widget.arguments["imageUrl"],
        child: Scaffold(
          backgroundColor: Colors.black,
          body: Center(
            child: AspectRatio(
              aspectRatio: 16 / 9,
              child: PhotoView(
                imageProvider: NetworkImage(widget.arguments["imageUrl"]),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

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

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

相关文章

多模态中各种Fusion方式汇总

多模态中各种Fusion骚操作 大噶好&#xff0c;我是DASOU&#xff1b; 今天继续写多模态系列文章&#xff0c;对多模态感兴趣的可以看我之前的文章&#xff1a; 其实对于多模态来说&#xff0c;主要可以从三个部分去掌握它&#xff1a; 如何获取多模态的表示【learning mult…

大数据毕业设计选题推荐-收视点播数据分析-Hadoop-Spark-Hive

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Spring基础(2):放弃XML,走向注解

上一篇并没有实际地带大家去看源码&#xff0c;而是介绍了两个概念&#xff1a; BeanDefinitionBeanPostProcessor 当然&#xff0c;我介绍得非常笼统&#xff0c;不论是BeanDefinition还是BeanPostProcessor其实都有着较为复杂的继承体系&#xff0c;种类也很多。作为Spring…

5.网络之IP

IP协议&#xff08;网络层&#xff09; 文章目录 IP协议&#xff08;网络层&#xff09;1. 报文格式2. IP地址2. 地址管理3. 特殊IP地址 IP协议&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;&#xff0c;是TCP/IP协议栈中最核心的协议之一&#xff0c;通过…

2024年天津财经大学珠江学院专升本预计新增金融学招生专业

2024年天津高职升本科天津财经大学珠江学院预计在今年新增招生专业&#xff0c;专业为金融学&#xff0c;目前该专业正在向天津市教育委员会申报中&#xff0c;预计最快下周即可在天津财经大学珠江学院招生官方发出通知。具体以官方审批是否通过为准。 珠江消息详情如下&#x…

01-单节点部署clickhouse及简单使用

1、下载rpm安装包&#xff1a; 官网&#xff1a;https://packages.clickhouse.com/rpm/stable/ clickhouse19.4版本之后只需下载3个rpm安装包&#xff0c;上传到节点目录即可 2、rpm包安装&#xff1a; 安装顺序为conmon->server->client 执行 rpm -ivh ./clickhouse-…

相机滤镜软件Nevercenter CameraBag Photo mac中文版特点介绍

Nevercenter CameraBag Photo mac是一款相机和滤镜应用程序&#xff0c;它提供了一系列先进的滤镜、调整工具和预设&#xff0c;可以帮助用户快速地优化和编辑照片。 Nevercenter CameraBag Photo mac软件特点介绍 1. 滤镜&#xff1a;Nevercenter CameraBag Photo提供了超过2…

HTML 表格

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>表格标签</title>/* <style>.yun {widt…

【MongoDB】索引 - 数组字段的多键索引

数组字段创建索引时&#xff0c;MongoDB会为数组中的每个元素创建索引键&#xff08;多键索引&#xff09;&#xff0c;多键索引支持数组字段的高效查询。 一、准备工作 这里准备一些数据 db.shop.insertMany([{_id: 1, name: "水果店1", fruits: ["apple&qu…

turtle绘制分形树-第10届蓝桥杯省赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第5讲。 turtle绘制分形树&…

Sui发布RPC2.0 Beta,拥抱GraphQL并计划弃用JSON-RPC

为了解决现有RPC存在的许多已知问题&#xff0c;Sui正在准备推出一个基于GraphQL的新RPC服务&#xff0c;名为Sui RPC 2.0。GraphQL是一种开源数据查询和操作语言&#xff0c;旨在简化需要复杂数据查询的API和服务。 用户目前可以访问Sui主网和测试网网络的Beta版本的只读快照…

基于侏儒猫鼬算法的无人机航迹规划-附代码

基于侏儒猫鼬算法的无人机航迹规划 文章目录 基于侏儒猫鼬算法的无人机航迹规划1.侏儒猫鼬搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用侏儒猫鼬算法来优化无人机航迹规划。 …

2024年人工智能安全发展十大预测

上周三&#xff0c;包括英国、美国和中国在内的近30个国家&#xff08;以及欧盟&#xff09;在人工智能安全峰会上达成首个全球性人工智能安全协议&#xff0c;并发布了《人工智能安全宣言》&#xff0c;这标志着人工智能正式进入安全发展的强监管时代。 峰会期间&#xff0c;…

网络爬虫的实战项目:使用JavaScript和Axios爬取Reddit视频并进行数据分析

概述 网络爬虫是一种程序或脚本&#xff0c;用于自动从网页中提取数据。网络爬虫的应用场景非常广泛&#xff0c;例如搜索引擎、数据挖掘、舆情分析等。本文将介绍如何使用JavaScript和Axios这两个工具&#xff0c;实现一个网络爬虫的实战项目&#xff0c;即从Reddit这个社交媒…

Spring boot集成sentinel限流服务

Sentinel集成文档 Sentinel控制台 Sentinel本身不支持持久化&#xff0c;项目通过下载源码改造后&#xff0c;将规则配置持久化进nacos中&#xff0c;sentinel重启后&#xff0c;配置不会丢失。 架构图&#xff1a; 改造步骤&#xff1a; 接着我们就要改造Sentinel的源码。…

【蓝桥杯省赛真题41】Scratch电脑开关机 蓝桥杯少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

目录 scratch电脑开关机 一、题目要求 编程实现 二、案例分析 1、角色分析

【数据开发】大数据平台架构,Hive / THive介绍

1、大数据引擎 大数据引擎是用于处理大规模数据的软件系统&#xff0c; 常用的大数据引擎包括Hadoop、Spark、Hive、Pig、Flink、Storm等。 其中&#xff0c;Hive是一种基于Hadoop的数据仓库工具&#xff0c;可以将结构化的数据映射到Hadoop的分布式文件系统上&#xff0c;并提…

✔ ★【备战实习(面经+项目+算法)】 11.6 学习

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…

【Vue.js】Vue3全局配置Axios并解决跨域请求问题

系列文章目录 文章目录 系列文章目录背景一、部署Axios1. npm 安装 axios2. 创建 request.js&#xff0c;创建axios实例3. 在main.js中全局注册axios4. 在页面中使用axios 二、后端解决跨域请求问题方法一 解决单Contoller跨域访问方法二 全局解决跨域问题 背景 对于前后端分离…

8 ICMP与ping

1、ICMP 协议的格式 一般在网络不同的时候&#xff0c;我们首先想到的是ping 那你知道 ping 是如何工作的吗&#xff1f; 1.ping 是基于 ICMP 协议工作的。 2.ICMP全称Internet Control Message Protocol&#xff0c;就是互联网控制报文协议。这里的关键在于"控制"…