【Flutter从入门到实战】⑯Flutter的动画、Animation-以心跳案例、交织动画(多个动画一起)-以矩形旋转变大小、FlutteriOS动画d出页面效果、Hero动画案例-图片放大展示

【Flutter从入门到实战】⑯Flutter的动画、Animation-以心跳案例、交织动画(多个动画一起)-以矩形旋转变大小、FlutteriOS动画d出页面效果、Hero动画案例-图片放大展示,第1张

Flutter从入门到实战
一共分为23个系列
①(Flutter、Dart环境搭建篇) 共3个内容 已更新
②(Dart语法1 篇) 共4个内容 已更新
③(Dart语法2 篇) 共2个内容 已更新
④(Flutter案例开发篇) 共4个内容 已更新
⑤(Flutter的StatelessWidget 共3个内容 已更新
⑥(Flutter的基础Widget篇) 共2个内容 已更新
⑦(布局Widget篇) 共1个内容 已更新
⑧(Flex、Row、Column以及Flexible、Stack篇) 共1个内容 已更新
⑨(滚动的Widget篇) 共4个内容 已更新
⑩(Dart的Future和网络篇) 共3个内容 已更新
⑪(豆瓣案例-1篇) 共3个内容 已更新
⑫(豆瓣案例-2篇) 共3个内容 已更新
⑬(Flutter渲染流程篇) 共3个内容 已更新
⑭(状态管理篇) 共3个内容 已更新
⑮(Flutter事件监听-以及路由使用篇) 共2个内容 已更新
⑯(Flutter的动画篇) 共4个内容 已更新

官方文档说明

官方视频教程
Flutter的YouTube视频教程-小部件


⑮、Flutter的动画篇 📚小贴士1. 随机生成网络图片网站推荐 ①、Animation1. Animation1.0 Animation的案例 - 以心跳动画作为案例1.0 效果图 - Animation的案例 - 以心跳动画作为案例1.1 Animation的案例 - 以心跳动画作为案例 - 动画完成执行完毕之后 恢复 和暂停 *** 作1.1 效果图 - Animation的案例 - 以心跳动画作为案例 - 动画完成执行完毕之后 恢复 和暂停 *** 作 2. AnimationWidget和AnimatedBuilder2.1 AnimationWidget的使用2.1.1 代码 AnimationWidget的使用 - 以心跳动画作为案例2.2 AnimatedBuilder的使用2.2.1 代码 AnimatedBuilder的使用 - 以心跳动画作为案例 2. 效果图 - 和Animation、AnimationWidget、AnimatedBuilder一样 ②、交织动画1. 交织动画 - 以一个矩形进行 `大小`、`颜色`、`旋转`、`透明度`改变做一个动画1.1 代码 - 交织动画 - 以一个矩形进行 `大小`、`颜色`、`旋转`、`透明度`改变做一个动画1.1 效果图 - 交织动画 - 以一个矩形进行 `大小`、`颜色`、`旋转`、`透明度`改变做一个动画 ③、动画d出页面3.1 iOS的Modald出页面3.1 效果图 - iOS的Modald出页面3.2 渐变动画d出3.2 代码 - 渐变动画d出3.2 效果图 - 渐变动画d出 ④、Hero动画1.Hero动画1.1 代码 - Hero动画 - 以列表图片 查看详情放大效果main.dartYHImageDetailsPage.dart 1.1 效果图 - Hero动画 - 以列表图片 查看详情放大效果

📚小贴士 1. 随机生成网络图片网站推荐

https://picsum.photos/
代码 https://picsum.photos/200/300?random=x
其中x是随机数

①、Animation

官方文档
速度曲线 https://api.flutter.dev/flutter/animation/Curves-class.html
Flutter动画包含4个类的知识点

Animation : 抽象类
监听动画值的改变 value
监听动画状态的改变 statusAnimationController继承Animation
vsync : 同步信号(this) -> with SingleTickerProviderStateMixin
forward() : 向前执行动画
reverse() : 反转执行动画curvedAnimation :
作用:设置动画执行的速率(速度曲线)Tween:设置动画执行的value的范围
begin : 开始值
end : 结束值
1. Animation 1.0 Animation的案例 - 以心跳动画作为案例
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

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

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

// with 混合类型 可以实现接口
// SingleTickerProviderStateMixin 必须是Stateful


/***
 * // 动画笔记
 *
 * class YHiOSHomePage extends StatefulWidget with SingleTickerProviderStateMixin {

 * Animation : 抽象类
 * 监听动画值的改变
 * 监听动画状态的改变
 * value
 * status
 * 2.AnimationController继承Animation
 * * vsync : 同步信号(this) -> with SingleTickerProviderStateMixin
 * * forward() : 向前执行动画
 * * reverse() : 反转执行动画
 * 3.curvedAnimation :
 * * 作用:设置动画执行的速率(速度曲线)
 * 4.Tween:设置动画执行的value的范围
 * * begin : 开始值
 *  end : 结束值
 *
 *  final controller =  AnimationController(vsync: this);
    final animation =  CurvedAnimation(parent: controller,curve: Curves.easeInOut);
    final valueAnimation =  Tween(begin: 100,end: 200).animate(animation);
 */



// 1.心跳动画
class YHHomePage extends StatefulWidget {
  const YHHomePage({Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> with SingleTickerProviderStateMixin {
  // 1.创建AnimationController
  late AnimationController _controller;
  late final Animation<double> animation;
  late final Animation sizeAnimation;

  @override
  void initState() {
    super.initState();

    // 设置CurvedAnimation 的时候 lowerBound 、upperBound 必须是0-1 默认就是0-1
    _controller = AnimationController(
        vsync: this,
      duration: Duration(seconds: 2),
      // lowerBound: 0.0,
      // upperBound: 1.0
    );

    // 2. 设置curved的值
    animation = CurvedAnimation(parent: _controller, curve: Curves.elasticInOut);

    // 3. Tween
    sizeAnimation = Tween(begin: 50.0,end: 150.0).animate(animation);
    
    //  监听动画值的改变 - 重新构建widget
    _controller.addListener(() {
      setState(() {

      });
    });

  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Icon(Icons.favorite,color: Colors.red,size: sizeAnimation.value,),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: (){
          // 向前动画
        _controller.forward();
        },
      ),
    );
  }
}






1.0 效果图 - Animation的案例 - 以心跳动画作为案例

演示案例 - 爱心变大效果

1.1 Animation的案例 - 以心跳动画作为案例 - 动画完成执行完毕之后 恢复 和暂停 *** 作
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

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

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

// with 混合类型 可以实现接口
// SingleTickerProviderStateMixin 必须是Stateful


/***
 * // 动画笔记
 *
 * class YHiOSHomePage extends StatefulWidget with SingleTickerProviderStateMixin {

 * Animation : 抽象类
 * 监听动画值的改变
 * 监听动画状态的改变
 * value
 * status
 * 2.AnimationController继承Animation
 * * vsync : 同步信号(this) -> with SingleTickerProviderStateMixin
 * * forward() : 向前执行动画
 * * reverse() : 反转执行动画
 * 3.curvedAnimation :
 * * 作用:设置动画执行的速率(速度曲线)
 * 4.Tween:设置动画执行的value的范围
 * * begin : 开始值
 *  end : 结束值
 *
 *  final controller =  AnimationController(vsync: this);
    final animation =  CurvedAnimation(parent: controller,curve: Curves.easeInOut);
    final valueAnimation =  Tween(begin: 100,end: 200).animate(animation);
 */



// 1.心跳动画
class YHHomePage extends StatefulWidget {
  const YHHomePage({Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> with SingleTickerProviderStateMixin {
  // 1.创建AnimationController
  late AnimationController _controller;
  late final Animation<double> _animation;
  late final Animation _sizeAnimation;

  // bool isForward; // 动画是否向前
  @override
  void initState() {
    super.initState();

    // 设置CurvedAnimation 的时候 lowerBound 、upperBound 必须是0-1 默认就是0-1
    _controller = AnimationController(
        vsync: this,
      duration: Duration(seconds: 2),
      // lowerBound: 0.0,
      // upperBound: 1.0
    );

    // 2. 设置curved的值
    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);

    // 3. Tween
    _sizeAnimation = Tween(begin: 50.0,end: 150.0).animate(_animation);
    
    //  监听动画值的改变 - 重新构建widget
    _controller.addListener(() {
      setState(() {

      });
    });


    // 监听动画的变化的改变
    _controller.addStatusListener((status) {
      // 动画完成时
      if (status == AnimationStatus.completed){
        _controller.reverse(); // 进行变小
      }
      else if (status == AnimationStatus.dismissed){
        //  动画销毁时 就继续变大
        _controller.forward();
      }
    });


  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: Icon(Icons.favorite,color: Colors.red,size: _sizeAnimation.value,),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: (){
          // 向前动画
        // _controller.forward();

        // 如果是动画是正在执行 那么 就停止动画
          if(_controller.isAnimating){
            _controller.stop();
          }
          else if (_controller.status == AnimationStatus.forward){
            // 如果动画是向前的 那么就继续向前
            _controller.forward();
          }
          else if (_controller.status == AnimationStatus.reverse){
            _controller.reverse();
          }
          else {
            _controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    // 回收
    _controller.dispose();
  }
}

1.1 效果图 - Animation的案例 - 以心跳动画作为案例 - 动画完成执行完毕之后 恢复 和暂停 *** 作

2. AnimationWidget和AnimatedBuilder

上面的代码的问题

每次写动画、都需要一段代码setState -> build
优化方案AnimationWidget
将需要执行动画的Widget放到一个AnimationWidget中的build方法里面进行返回
缺点
1.每次都需要创建一个类
2.如果构建的Widget有子类,那么子类依然会重复构建AnimationBuilder 开发用的比较多
解决AnimationWidget的缺点
2.1 AnimationWidget的使用

创建一个类 继承于 AnimationWidget
必须调用父类方法super(listenable)

2.1.1 代码 AnimationWidget的使用 - 以心跳动画作为案例
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

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

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


// 1.心跳动画
class YHHomePage extends StatefulWidget {
  const YHHomePage({Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late final Animation<double> _animation;
  late final Animation _sizeAnimation;

  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),

    );

    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
    _sizeAnimation = Tween(begin: 50.0,end: 150.0).animate(_animation);
    //  监听动画值的改变 - 重新构建widget
    _controller.addListener(() {
      setState(() {

      });
    });
    // 监听动画的变化的改变
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed){
        _controller.reverse(); 
      }
      else if (status == AnimationStatus.dismissed){
        _controller.forward();
      }
    });


  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: YHAnimationIcon(_sizeAnimation), // ⭐️需要做动画的Widget继承于AnimatedWidget 这样性能更高、不会重复setState 和 build
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: (){

          if(_controller.isAnimating){
            _controller.stop();
          }
          else if (_controller.status == AnimationStatus.forward){
            _controller.forward();
          }
          else if (_controller.status == AnimationStatus.reverse){
            _controller.reverse();
          }
          else {
            _controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    // 回收
    _controller.dispose();
  }
}

/**
 * AnimatedWidget
 * 缺点
 * 1.每次都需要创建一个类
   2.如果构建的Widget有子类,那么子类依然会重复构建
 * */
class YHAnimationIcon extends AnimatedWidget {
  final Animation _sizeAnim; // 可以从外部传递过来

  YHAnimationIcon(this._sizeAnim):super(listenable: _sizeAnim);
  // YHAnimationIcon(Animation anim):super(listenable: anim); // anim 来自于父类
  // @override
  Widget build(BuildContext context) {
    //   Listenable anim = listenable;
    // 把需要动画的Widget放到这里
    return Icon(Icons.favorite,color: Colors.red,size: _sizeAnim.value,);
  }
}



2.2 AnimatedBuilder的使用

解决了AnimationWidget的缺点
不会重新SetState和build

2.2.1 代码 AnimatedBuilder的使用 - 以心跳动画作为案例
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}
// 1.心跳动画
class YHHomePage extends StatefulWidget {
  const YHHomePage({Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> with SingleTickerProviderStateMixin {
  // 1.创建AnimationController
  late AnimationController _controller;
  late final Animation<double> _animation;
  late final Animation _sizeAnimation;

  // bool isForward; // 动画是否向前
  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),

    );
    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);
    _sizeAnimation = Tween(begin: 50.0,end: 150.0).animate(_animation);

    //  监听动画值的改变 - 重新构建widget
    _controller.addListener(() {
      // 使用AnimatedBuilder 不需要setState 同样能执行动画
      // setState(() {
      //
      // });
    });


    // 监听动画的变化的改变
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed){
        _controller.reverse();
      }
      else if (status == AnimationStatus.dismissed){
        _controller.forward();
      }
    });


  }

  @override
  Widget build(BuildContext context) {
    print("执行了_YHHomePageState 的build 方法");
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child: AnimatedBuilder(
          builder: (ctx,child){
            return Icon(Icons.favorite,color: Colors.red,size: _sizeAnimation.value,);
          },
          animation: _controller,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: (){
          if(_controller.isAnimating){
            _controller.stop();
          }
          else if (_controller.status == AnimationStatus.forward){
            _controller.forward();
          }
          else if (_controller.status == AnimationStatus.reverse){
            _controller.reverse();
          }
          else {
            _controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    // 回收
    _controller.dispose();
  }
}




2. 效果图 - 和Animation、AnimationWidget、AnimatedBuilder一样


②、交织动画

把动画放在一起 叫做交织动画
Staggered animations - 官方文档
就是监听多个Tween的值改变

1. 交织动画 - 以一个矩形进行 大小颜色旋转透明度改变做一个动画 1.1 代码 - 交织动画 - 以一个矩形进行 大小颜色旋转透明度改变做一个动画
import 'dart:math';
import 'package:flutter/material.dart'; // runApp在这个material库里面

main() => runApp(MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: YHHomePage(),
    );
  }
}
// 1.心跳动画
class YHHomePage extends StatefulWidget {
  const YHHomePage({Key? key}) : super(key: key);

  @override
  State<YHHomePage> createState() => _YHHomePageState();
}

class _YHHomePageState extends State<YHHomePage> with SingleTickerProviderStateMixin {
  // 1.创建AnimationController
  late AnimationController _controller;
  late final Animation<double> _animation;

  late final Animation _sizeAnim;
  late final Animation _colorAnim;
  late final Animation _opacityAnim;
  late final Animation _radiansAnim;


  @override
  void initState() {
    super.initState();

    _controller = AnimationController(
      vsync: this,
      duration: Duration(seconds: 2),

    );
    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);

    // 3. Tween 的 animate 有些是不支持设置CurvedAnimation。
    //     _sizeAnim = Tween(begin: 10.0,end: 200.0).animate(_animation); 最好不要这样写 可能会导致报错
    // 3.1 创建size的 Tween 大小
    _sizeAnim = Tween(begin: 10.0,end: 200.0).animate(_controller);
    // 3.2 创建color的 Tween 颜色
    _colorAnim = ColorTween(begin: Colors.orange,end: Colors.blue).animate(_controller);
    // 3.1 创建opacity的 Tween 透明度
    _opacityAnim = Tween(begin: 0.0,end: 1.0).animate(_controller);
    // 3.1 创建radians的 Tween 旋转幅度
    _radiansAnim = Tween(begin: 0.0,end: 2 *pi).animate(_controller);


    //
    // _controller.addListener(() {
    //   setState(() {
    //
    //   });
    // });

    // 监听动画的变化的改变
    _controller.addStatusListener((status) {
      if (status == AnimationStatus.completed){
        _controller.reverse();
      }
      else if (status == AnimationStatus.dismissed){
        _controller.forward();
      }
    });


  }

  @override
  Widget build(BuildContext context) {
    print("执行了_YHHomePageState 的build 方法");

    /**
     * 效果
     * 1. 大小变化动画
     * 2. 颜色变化动画
     * 3. 透明度变化动画
     * */
    return Scaffold(
      appBar: AppBar(
        title: Text("首页"),
      ),
      body: Center(
        child:AnimatedBuilder(
          builder: (ctx,child){
            return Opacity(
              opacity: 0.5, // 透明度
              //  将需要旋转的Widget用Transform包裹
              child: Transform(
                transform: Matrix4.rotationZ(_radiansAnim.value), // pi是180
                alignment: Alignment.center,
                child: Container(
                  width: _sizeAnim.value,
                  height: _sizeAnim.value,
                  color: _colorAnim.value,
                ),
              ),
            );
          },
          animation: _controller,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.play_arrow),
        onPressed: (){
          if(_controller.isAnimating){
            _controller.stop();
          }
          else if (_controller.status == AnimationStatus.forward){
            _controller.forward();
          }
          else if (_controller.status == AnimationStatus.reverse){
            _controller.reverse();
          }
          else {
            _controller.forward();
          }
        },
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    super.dispose();
    // 回收
    _controller.dispose();
  }
}





1.1 效果图 - 交织动画 - 以一个矩形进行 大小颜色旋转透明度改变做一个动画

③、动画d出页面 3.1 iOS的Modald出页面

fullscreenDialog: true // 以模态方式d出

import 'package:flutter/material.dart';
import 'package:learn_flutter/douban/pages/main/main.dart'; // runApp在这个material库里面
import 'pages/modal_page.dart';
main() => runApp(MyApp());

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

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

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("学习模板_Widget"),
      ),
      body: Center(
        child: Text("hello world"),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.pool),
        onPressed: (){
          Navigator.of(context).push(MaterialPageRoute(builder: (ctx){
            return YHModalPage();
          },
            fullscreenDialog: true // 以模态方式d出

          ));
        },
      ),
    );
  }
}






3.1 效果图 - iOS的Modald出页面

3.2 渐变动画d出 3.2 代码 - 渐变动画d出

使用 PageRouteBuilder

import 'package:flutter/material.dart';
import 'package:learn_flutter/douban/pages/main/main.dart'; // runApp在这个material库里面
import 'pages/modal_page.dart';
main() => runApp(MyApp());

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

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

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("学习模板_Widget"),
      ),
      body: Center(
        child: Text("hello world"),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.pool),
        onPressed: (){
          // 渐变动画d出
          Navigator.of(context).push(PageRouteBuilder(
            transitionDuration: Duration(seconds: 1),
              pageBuilder: (ctx,animation1,animation2){
            return FadeTransition(
              opacity: animation1,
              child: YHModalPage(),
            );
          }));
        },
      ),
    );
  }
}





3.2 效果图 - 渐变动画d出

④、Hero动画 1.Hero动画

Hero动画官方视频教程
Hero动画官方文档
官方是这样说明的
hero指的是在屏幕之间飞行的小部件。
使用 Flutter 的 Hero 小部件创建英雄动画。
将英雄从一个屏幕飞到另一个屏幕。
将英雄的形状从圆形转换为矩形,同时将其从一个屏幕飞到另一个屏幕。
Flutter 中的 Hero 小部件实现了一种通常称为共享元素转换或 共享元素动画的动画样式。

1.1 代码 - Hero动画 - 以列表图片 查看详情放大效果 main.dart
import 'package:flutter/material.dart';
import 'package:learn_flutter/day12_animation/pages/image_details.dart';
import 'package:learn_flutter/douban/pages/main/main.dart'; // runApp在这个material库里面
import 'pages/modal_page.dart';
main() => runApp(MyApp());

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

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

class YHiOSHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("学习模板_Widget"),
      ),
      body: Center(
        child: GridView(
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 2,
            crossAxisSpacing: 8,
            mainAxisSpacing: 8,
            childAspectRatio: 16/9,
          ),
          children: List.generate(20, (index){
            final imageUrl = "https://picsum.photos/500/500?random=${index}";


            return GestureDetector(
              onTap: (){
                // MaterialPageRoute 看起来像push效果
                // 换成PageRouteBuilder 做成一个动画渐变效果
                Navigator.of(context).push(PageRouteBuilder(pageBuilder: (ctx,anim1,anim2){
                  return FadeTransition(opacity: anim1, child: YHImageDetailsPage(imageUrl));
                }));
              },
                child: Hero(
                  tag:imageUrl,
                    child: Image.network(imageUrl,fit: BoxFit.cover,)));
          }),
        ),
      ),
    );
  }
}





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

class YHImageDetailsPage extends StatelessWidget {

  final String _imageUrl;
  YHImageDetailsPage(this._imageUrl);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: Center(
        child: GestureDetector(
          onTap: (){
            Navigator.of(context).pop();
          },
            child: Hero(tag:_imageUrl, child: Image.network(_imageUrl))),
      ),
    );
  }
}


1.1 效果图 - Hero动画 - 以列表图片 查看详情放大效果

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/996703.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存