Flutter學習(五) 基礎控制元件 以及ios的Cupertino風格
前幾篇文章講了一些基礎widget 的使用方式,這篇文章會介紹flutter 動畫部分的簡單使用.
1. Animation
1.AnimationController
AnimationController會產生數值0--1區間內的數值變換,也可以控制動畫 reverse(),forward(),animateTo()等
構造引數中vsync
繫結管理動畫執行的定時器,widget不顯示時暫停動畫,顯示時回覆動畫,Duration
設定動畫時間
AnimationController _animController =
AnimationController(vsync: this, duration: Duration(seconds: 2))
..reverse();
複製程式碼
2.Tween 補間動畫
如果你想產生 0--1 之外的數值變換 可以使用Tween
來實現,當前Tween 不僅僅只能是數值型別的 可以是顏色 曲線等型別ColorTween CurveTween
具體可以檢視官網API文件說明. 使用時需要呼叫animate()
方法去傳入控制器,這裡傳入的引數是 Animation所以 除了控制器也可以去傳入一個 Animation的動畫,另外也可以使用 addListener() addStatusListener()
去監聽動畫執行過程 以及狀態,通過 value()
去獲取當前的數值 作用到 widget上
//產生數值
AnimationController _animController =
AnimationController(vsync: this, duration: Duration(seconds: 2));
_animtion = Tween<double>(begin:100.0, end:
final Animation curve =new CurvedAnimation(parent: _animController, curve: Curves.easeOut);
200.0).animate(curve)
..addListener(() {
setState(() {
_currentValue = _animtion.value;
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animController.reverse();
} else if (status == AnimationStatus.forward) {
_animController.forward();
}
});
//使用
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
_animController.forward();
}),
body: Center(
child: Container(
width: _currentValue,
height: _currentValue,
color: Colors.blue,
child: Text('$_currentValue'),
),
),
);
}
複製程式碼
2.AnimatedWidget
使用時繼承這個Widget,在使用 Tween
動畫時 在數值產生時需要去呼叫setState((){})
去 重繪佈局, 如果使用AnimatedWidget
可以省略這部 它內部會直接對狀態更新進行管理.
controller 和 animation 從外部傳入,建立方式跟Twee
一樣 ,在構造中傳入 anim 物件
//控制器物件建立
@override
void initState() {
super.initState();
_animController =
AnimationController(vsync: this, duration: Duration(seconds: 2));
_animtion = Tween<double>(begin: 100.0, end: 200.0).animate(_animController)
..addListener(() {
setState(() {
_currentValue = _animtion.value;
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
_animController.reverse();
} else if (status == AnimationStatus.forward) {
_animController.forward();
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
_animController.forward();
}),
body: MyLogo(
anim: _animtion,
),
);
}
//動畫widget
class MyLogo extends AnimatedWidget {
MyLogo({Key key, @required Animation anim}) : super(listenable: anim);
final Tween<double> _rotateAnim = Tween<double>(begin: 0.0, end: 100.0);
final Tween<double> _scaleAnim = Tween<double>(begin: 1.0, end: 10.0);
@override
Widget build(BuildContext context) {
Animation anim = listenable;
return Center(
child: Transform.rotate(
angle: _rotateAnim.evaluate(anim),
child: Transform.scale(
scale: _scaleAnim.evaluate(anim),
child: Container(
/* width: anim.value,
height: anim.value,*/
child: FlutterLogo(),
),
),
),
);
}
}
複製程式碼
3.AnimatedBuilder
分離物件與Widget,使用AnimatedBuilder將動畫描述為另一個widget的build方法的一部分,controller animation 建立與前面一致
@override
Widget build(BuildContext context) {
double _screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
floatingActionButton: FloatingActionButton(onPressed: () {
_animController.forward();
}),
body: Center(
child: AnimatedBuilder(
animation: _animtion,
builder: (context, child) {
return Transform(
child: Container(width: 200, height: 200, child: FlutterLogo()),
transform: Matrix4.translationValues(
_screenWidth * _animtion.value, 0, 0),
);
}),
),
);
}
複製程式碼
3.並行動畫
可以對一個widget 同時做動畫 透明度和 寬高改變
class AnimatedLogo extends AnimatedWidget {
// The Tweens are static because they don't change.
static final _opacityTween = new Tween<double>(begin: 0.1, end: 1.0);
static final _sizeTween = new Tween<double>(begin: 0.0, end: 300.0);
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return new Center(
child: new Opacity(
opacity: _opacityTween.evaluate(animation),
child: new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
child: new FlutterLogo(),
),
),
);
}
}
複製程式碼
4. 自定義繪製Widget
在原生 java 中 自定義控制元件 需要用到 畫筆paint
畫布canvas
在flutter中也是用這些來繪製頁面, 使用的時候需要用到CustomPaint
包裹自己定義的Painter
,paint canvas的使用與java 類似就不在贅述
//使用方式
child: CustomPaint(
size: Size(30, 30),
painter: HeartPainter(),
),
//繪製頁面
class HeartPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
Paint paint = Paint();
paint
..color = Color(0xffff0000)
..style = PaintingStyle.fill
..strokeWidth = 5;
double width = size.width;
double height = size.height;
Path path = Path();
path.moveTo(0.5 * width, height * 0.35);
path.cubicTo(0.2 * width, height * 0.1, -0.25 * width, height * 0.6,
0.5 * width, height);
path.moveTo(0.5 * width, height * 0.35);
path.cubicTo(0.8 * width, height * 0.1, 1.25 * width, height * 0.6,
0.5 * width, height);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
複製程式碼
5.Transform 矩陣變換
Transform 可以對子widget 做一些矩陣變化,達到一定的效果
- Transform.translate 平移
- Transform.rotate 旋轉
- Transform.scale 縮放
- ....
6. Hero
類似原生中的頁面共享動畫,在頁面A用Hero 包裹想要進行動畫的 widget 並設定tag ,在跳轉之後的B頁面 同樣適用Hero 包裹widget 設定相同的 tag
Hero(
tag: "avatar", //唯一標記,前後兩個路由頁Hero的tag必須相同
child: ClipOval(
child: Image.asset("images/avatar.png",
width: 50.0,
),
),
複製程式碼
轉場動畫
Navigator.push(context, CupertinoPageRoute(
builder: (context)=>PageB(),
));
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 500), //動畫時間為500毫秒
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) {
return new FadeTransition(
//使用漸隱漸入過渡,
opacity: animation,
child: PageB(), //路由B
);
},
),
);
複製程式碼