Flutter 动画详解系列——Flutter自定义动画
为什么要使用TweenAnimationBuilder?假设您要创建一个基本的动画:一个不会永远重复的动画,它只是一个widget或widget 树。
TweenAnimationBuilder自定义隐式动画
tween:Twee类型,动画过程中会把 Tween 的中间插值传给 builder 来构建子组件,从而可以实现过渡动画效果。
builder:组件构建方法,类型为ValueWidgetBuilder,具体定义如下,其中 value 参数就是 tween 动画过程中的中间插值。也就是我们在动画期间,会不断调用builder 重新绘制子组件。
大小变化的动画:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
bool flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('大小变化'),
),
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,
color: Colors.red,
size: value,
);
}),
),
),
);
}
}

效果:点击浮动按钮,五角星的大小会变化。
透明度变化的动画:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State {
bool flag = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('透明度变化'),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.refresh),
onPressed: () {
setState(() {
flag = !flag;
});
},
),
body: Center(
child: TweenAnimationBuilder(
tween: Tween(begin: 0.0, end: flag ? 0.2 : 1.0),
duration: const Duration(seconds: 1),
builder: ((context, value, child) {
return Opacity(
opacity: value,
child: Container(color: Colors.blue, width: 200, height: 200),
);
}),
),
),
);
}
}

效果:点击浮动按钮,盒子的透明度会变化。
AnimatedBuilder自定义显式动画
透明度动画:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State
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('透明度动画'),
),
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.blue,
child: const Text('我是一个Text组件'),
),
);
},
),
),
);
}
}

效果:盒子的透明度会自动不停变化。
自定义变化范围:
上面代码中 opacity 的值我们也可以使用 Tween 来设置:
opacity: Tween(begin: 0.5, end: 1.0).animate(_controller).value, //从0.5到1变化
位置变化:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State createState() => _HomePageState();
}
class _HomePageState extends State
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 y = Tween(begin: -120.0, end: 120.0)
.chain(CurveTween(curve: Curves.easeIn))
// .chain(CurveTween(curve: const Interval(0.2, 0.6)))
.animate(_controller);
return Scaffold(
appBar: AppBar(
title: const Text('位置变化'),
),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget? child) {
return Container(
width: 200,
height: 200,
color: Colors.blue,
transform: Matrix4.translationValues(0, y.value, 0),
child: const Text('我是一个Text组件'),
);
},
),
),
);
}
}