android Flutter:动画

wuchangjian2021-11-14 13:50:29编程学习

本节学习老孟《Flutter 实战入门》。问题:为什么我代码的动画,没有一个时动起来,问题出现哪里。
动画实现得基本原理是:将一定顺序得YUI界面连续显示出来,借助人得视觉暂留现象,从而达到连续运动得效果。动画与电影得原理是一样得,动画系统中一个重要得指标是帧率fps(Frame Per Second),即每秒得帧数。对于人眼来说,帧率超过24PS就会感觉比较顺滑;而Flutter中,理论上可以实现60FPS。
一、动画简介
1、Animation
Animation是一个抽象(abstract)类,它不能直接实例化,比较常用得Animation类是Animation。Animation对象再一段时间内生成一个区间值。Animation得输出可以是线性得,也可以是非线性得,因此Animation本身和UI渲染没有任何关系,它拥有动画当前值和状态。通过给Animation对象添加监听器,可以监听动画得每一帧及动画状态。添加监听得方法有:

addListener():每一帧都会调用,一般会在其中调用setState()来触发UI重建;
addStatusListener():添加动画状态改变监听器,当动画状态开始
结束、正向、反向发生变化时调用。

2、Curve
动画得过程时线性得还是非线性得是由Curve确定得,Curve得作用和android中得Interpolator(插值)时一样得,负责控制动画变化得速率,通俗地讲就是使动画得效果能够以匀速、加速、抛物线等各种速率变化。
在这里插入图片描述
使用CurvedAnimation来指定动画曲线,代码如下:

animation = CurvedAnimation(parent: animationController, curve: Curves.bounceIn),

3、AnmationController
AnmationController使动画控制器,控制动画得播放、停止等。AnmationController继承自Animation,是一个特殊得Animation对象,屏幕刷新得每一帧都会生成一个新得值,默认情况下它会线性地生成0.0~1.0的值。

AnimationController animationController;
animationController = AnimationController(
      lowerBound: -widget.actionsWidth,
      upperBound: 0,
      vsync: this,
      duration: Duration(milliseconds: 300)
    )

4、Tween
AnimationController继承自Anmation,输出的值只能为double类型。如果要求动画的效果是颜色变化,这时候AnimationController并不能满足需求,而Tween继承自Animatable,它提供了evaluate方法以获取当前映射值。使用Tween对象需要调用animate方法传入控制器对象,并返回一个animation。

//从白色渐变到黑色
ColorTween(begin: Colors.white,end: Colors.black);
//控件大小变化
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  AnimationController animationController;
  Animation<double> animation;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: Duration(seconds: 1),
        lowerBound: 0.0,
        upperBound: 1.0,
        vsync: this,
    )..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //执行结束反向执行
        animationController.reverse();
      }else if (status == AnimationStatus.dismissed) {
        //反向执行结束正向执行
        animationController.forward();
      }
    });
    animation = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
    animation = Tween(begin: 80.0,end: 200.0,).animate(animationController);
    animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height: animation.value,
        width: animation.value,
        color: Colors.green,
      ),
    );
    throw UnimplementedError();
  }

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

三、AnimatedWidget
AnimatedWidget 封装了setState()方法部分。修改后代码:

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  AnimationController animationController;
  Animation<double> animation;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: Duration(seconds: 1),
        lowerBound: 0.0,
        upperBound: 1.0,
        vsync: this,
    )..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //执行结束反向执行
        animationController.reverse();
      }else if (status == AnimationStatus.dismissed) {
        //反向执行结束正向执行
        animationController.forward();
      }
    });
    animation = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
    animation = Tween(begin: 0.0,end: 200.0,).animate(animationController);
    animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      animation : animation,
    );
    throw UnimplementedError();
  }

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

class AnimatedContainer extends AnimatedWidget {
  AnimatedContainer({Key key,Animation<double> this.animation} )
  : super(key:key,listenable:animation);
  final Animation<double> animation;
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height:animation.value,
        width: animation.value,
        color: Colors.green,
      ),
    );
    throw UnimplementedError();
  }

}

四、AnimatedBuilder

//改变其子控件大小
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  AnimationController animationController;
  Animation<double> animation;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: Duration(seconds: 1),
        lowerBound: 0.0,
        upperBound: 1.0,
        vsync: this,
    )..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //执行结束反向执行
        animationController.reverse();
      }else if (status == AnimationStatus.dismissed) {
        //反向执行结束正向执行
        animationController.forward();

      }
    });
    animation = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
    animation = Tween(begin: 0.0,end: 200.0,).animate(animationController);
    animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: _AnimatedBuilder (
        animation: animation,
        child : FlutterLogo(),
      ),
    );
    throw UnimplementedError();
  }

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

class _AnimatedBuilder extends StatelessWidget {
  _AnimatedBuilder({this.child,this.animation});
  final Animation<double> animation;
  final Widget child;
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: animation,
        builder: (context,child) {
          return Container(
            height: animation.value,
            width: animation.value,
            child: child,
          );
        },
    child: child,);
    throw UnimplementedError();
  }

}

AnimatedWidget 和 AnimatedBuilder的区别:
(1)AnimatedBuilder继承自AnimatedWidget,所以AnimatedWidget能实现的功能AnimatedBuilder也能实现,但AnimatedBuilder的功能更强大。
(2)AnimatedWidget可以认为是Animation的助手类,它封装了监听Animation对象的通知并调用了setState.

五、交错动画
交错动画是由多个动画组成,多个动画可以同时执行,也可以顺序执行。这些动画由一个AnimationController控制,动画是同时执行还是顺序执行由动画对象的Interval属性决定。

//控件大小和颜色同时变化
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  AnimationController animationController;
  Animation<double> animationSize;
  Animation animationColor;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: Duration(seconds: 1),
        lowerBound: 0.0,
        upperBound: 1.0,
        vsync: this,
    ) ..addListener(() { })
      ..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //执行结束反向执行
        animationController.reverse();
      }else if (status == AnimationStatus.dismissed) {
        //反向执行结束正向执行
        animationController.forward();

      }
    });
//     animationColor = animationSize = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
    animationSize = Tween(begin: 0.0,end: 1.0,).animate(animationController);
    animationColor = ColorTween(begin: Colors.yellow,end: Colors.blue).animate(animationController);
    animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height:( animationSize.value+ 1) * 100,
        width: ( animationSize.value + 1) * 100,
        color: animationColor.value,
      ),
    );
    throw UnimplementedError();
  }

  @override
  void dispose() {
    super.dispose();
    animationController.dispose();
  }
}
//控件大小和颜色变化按照顺序执行
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  AnimationController animationController;
  Animation<double> animationSize;
  Animation animationColor;
  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      duration: Duration(seconds: 1),
        lowerBound: 0.0,
        upperBound: 1.0,
        vsync: this,
    ) ..addListener(() { })
      ..addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //执行结束反向执行
        animationController.reverse();
      }else if (status == AnimationStatus.dismissed) {
        //反向执行结束正向执行
        animationController.forward();

      }
    });
//     animationColor = animationSize = CurvedAnimation(parent: animationController, curve: Curves.easeIn);
    animationSize = Tween(begin: 80.0,end: 100.0,).animate(CurvedAnimation(parent: animationController,curve: Interval(0.0,0.5)));
    animationColor = ColorTween(begin: Colors.yellow,end: Colors.blue).animate(CurvedAnimation(parent: animationController,curve:Interval(0.5,1.0)) );
    animationController.forward();
  }
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        height:animationSize.value,
        width: animationSize.value,
        color: animationColor.value,
      ),
    );
    throw UnimplementedError();
  }

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

六、AnimatedList
AnimatedList:列表中数据发生变化时加入过渡动画。AnimatedList得构建首先需要itemBuilder,itemBuilder是一个函数,列表的每一个索引会调用,这个函数有一个animation参数,可以设置成任何一个动画。如果初始的时候数据不为空,AnimatedList的构建需要initialItemCount,initialItemCount表示数据的数量。当有数据添加或者删除时,调用AnimatedListState的对应的方法:

AnimatedListState.insertItem
AnimatedListState.removeItem

得到AnimatedListState有两个方法:
(1)AnimatedList.of(context)方法:

 AnimatedList.of(context).insertItem(index)
 AnimatedList.of(context).removeItem(index, (context, animation) => {})

(2)设置key,

 final GlobalKey<AnimatedListState> _kiytKey = GlobalKey<AnimatedListState>();
  AnimatedList(
      key: _kiytKey,
      initialItemCount:_list.length,
      itemBuilder: (BuildContext context, int index, Animation animation) {
        return _buildItem(_list[index].toString(),animation);
         },
      );

调用如下:

_listKey.currentState.insertItem(_index);
//左进右出动画效果
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: AnimationDemo(),
    );
  }
}

class AnimationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _AnimationDemo();
  }

}

class _AnimationDemo extends State<AnimationDemo> with SingleTickerProviderStateMixin{
  List<int> _list = [];
  final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();

  void _addItem() {
    final int _index = _list.length;
    _list.insert(_index, _index);
    _listKey.currentState.insertItem(_index);
  }

  void _removeItem() {
    final int _index = _list.length - 1;
    var item =_list[_index].toString();
    _listKey.currentState.removeItem((_index), (context, animation) => _buildItem(item,animation));
    _list.removeAt(_index);
  }

  Widget _buildItem(String _item,Animation _animation) {
    return SlideTransition(
        position: _animation.drive(CurveTween(curve: Curves.easeIn)).drive(Tween<Offset>(begin: Offset(1,1),end: Offset(0,1))),
      child: Card(
        child: ListTile(
          title: Text(_item),
        ),
      ),
    );
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: AnimatedList(
        key: _listKey,
        initialItemCount: _list.length,
        itemBuilder: (BuildContext context,int index,Animation animation) {
          return _buildItem(_list[index].toString(), animation);
        },
      ),

      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          FloatingActionButton(
            onPressed: ()=>_addItem(),
            child: Icon(Icons.add),
          ),
          SizedBox(width: 60,),
          FloatingActionButton(
            onPressed: ()=>_removeItem(),
            child: Icon(Icons.remove),),

        ],
      ),
    );
    throw UnimplementedError();
  }

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

在这里插入图片描述
七、Hero
Hero是一种常见的动画效果,是可以再路由(页面)之间“飞行”的widget,可以从一个页面打开另一个页面时产生一个简单的过滤动画。构建Hero需要一个子控件,这里设置其为图片;还需要一个tag,第一个页面喝第二个页面的Hero控件的atg属性值要保持一致。

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HeroDemo(),
    );
  }
}

class HeroDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return GridView.builder(
        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2),
        itemBuilder: (context,index)=>_buildItem(context,index),
        itemCount: 1,);
    throw UnimplementedError();
  }
  _buildItem(context,index) {
    return GestureDetector(
      onTap: () {
        print("lililililili");
        Navigator.of(context).push(MaterialPageRoute(builder: (context){
          return HeroDetailDemo();
        }));
      },
      child: Hero(
        tag: 'chair',
        child: Container(
          height: 100,
          width: 100,
          child: Image.asset('assets/icons/ic_launcher_test.png'),
        ),
      ),
    );
  }
}

class HeroDetailDemo extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return AspectRatio(
        aspectRatio: 1,
        child: Hero(
          tag: 'chair',
          child: Image.asset('assets/icons/ic_launcher_test.png'),
        ),
    );
    throw UnimplementedError();
  }

}

相关文章

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。