-
Flutter Animation Widget
Animation Widget按照能否管理其生命周期(AnimationController)分为显式动画和隐式动画,以及自定义动画
-
隐式动画
- 内置隐式动画
- 自定义隐式动画
-
显式动画
- 内置显式动画
- 自定义显式动画AnimationWidget、AnimationBuilder
-
CustomPainter(Canvas动画)
-
内置隐式动画AnimatedFoo
- Foo为没有动画是Widget类的名字。
- 隐式动画是一次性动画,每当动画值改变后,会执行一次
- 不需要创建controller,不能控制生命周期
系统内置隐式动画列表:
-
AnimatedAlign
-
AnimatedContainer
-
AnimatedCrossFade
-
AnimatedDefaultTextStyle
-
AnimatedIcon(需要controller是显式动画)
-
AnimatedList
-
AnimatedModalBarrier
-
AnimatedOpacity
-
AnimatedPadding
-
AnimatedPhysicalModel
-
AnimatedPositioned
-
AnimatedPositionedDirectional
-
AnimatedSize
-
AnimatedSwitcher
示例AnimatedOpacity:
main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedFoo'),
),
body: const Center(child: FooOpacity()),
);
}
}
class FooOpacity extends StatefulWidget {
const FooOpacity({Key? key}) : super(key: key);
@override
State<FooOpacity> createState() => _FooOpacityState();
}
class _FooOpacityState extends State<FooOpacity> {
double _opacity = 0;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 400,
height: 400,
child: Column(
children: [
TextButton(
onPressed: () {
setState(() {
_opacity = _opacity > 0 ? 0 : 1;
});
},
child: const Text("play")),
Expanded(
child: AnimatedOpacity(
opacity: _opacity,
duration: const Duration(seconds: 5),
child: Container(
color: Colors.red,
),
))
],
),
);
}
}
示例AnimatedAlign:
import 'package:flutter/material.dart';
main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedFoo'),
),
body: const Center(child: FooAlign()),
);
}
}
class FooAlign extends StatefulWidget {
const FooAlign({Key? key}) : super(key: key);
@override
State<FooAlign> createState() => _FooAlignState();
}
class _FooAlignState extends State<FooAlign> {
AlignmentGeometry _alignmentGeometry = Alignment.topLeft;
@override
Widget build(BuildContext context) {
return SizedBox(
width: 400,
height: 400,
child: Column(
children: [
TextButton(
onPressed: () {
setState(() {
_alignmentGeometry = _alignmentGeometry == Alignment.topLeft?Alignment.bottomRight:Alignment.topLeft;
});
},
child: const Text("play")),
Expanded(
child:AnimatedAlign(alignment: _alignmentGeometry, duration: const Duration(seconds: 1),child: Container(
width: 40,
height: 40,
color: Colors.red,),))
],
)
);
}
}
-
自定义隐式动画
系统提供的隐式动画不能满足的话,基于TweenAnimationBuilder进行自定义动画
示例自定义旋转:
import 'package:flutter/material.dart';
import 'dart:math';
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
double beginAngle = 0;
double endAngle = 2 * pi;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('TweenAnimationBuilder'),
),
body: RotationIcon(
duration: const Duration(seconds: 2),
beginRotate: beginAngle,
endRotate: endAngle,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 点击事件,设置动画开始
setState(() {
double tmp = beginAngle;
beginAngle = endAngle;
endAngle = tmp;
});
},
child: const Icon(Icons.play_arrow),
),
);
}
}
class RotationIcon extends StatelessWidget {
final Duration duration;
final double beginRotate;
final double endRotate;
const RotationIcon({Key? key, required this.duration, required this.beginRotate, required this.endRotate}) : super(key: key);
@override
Widget build(BuildContext context) {
return TweenAnimationBuilder<double>(
tween: Tween<double>(begin: beginRotate, end: endRotate),
duration: const Duration(seconds: 2),
builder: (BuildContext context, double angle, Widget? child) {
return Transform.rotate(
angle: angle,
child: child,
);
},
child: const FlutterLogo(
size: 80,
));
}
}
-
内置显式动画
- 需要创建controller,可以控制动画循环、暂停等
- 可创建线性动画TweenAnimation或者非线性动画CurveAnimation
-
RotationTransition
-
FadeTransition
-
ScaleTransition
-
SizeTransition
-
SideTransition
-
PositionedTransition
-
DecoratedBoxTransition
-
AnimatedIcon
-
DefaultTextStyleTransition
-
RelativePositionedTransition
示例RotationTransition 线性动画:
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
/// 会重复播放的控制器
late final AnimationController _repeatController;
/// 线性动画
late final Animation<double> _animation;
@override
void initState() {
super.initState();
/// 动画持续时间是 3秒,此处的this指 TickerProviderStateMixin
_repeatController = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
)..repeat(); // 设置动画重复播放
// 创建一个从0到360弧度的补间动画 v * 2 * π
_animation = Tween<double>(begin: 0, end: 1).animate(_repeatController);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('RotationTransition--Tween'),
),
body: Center(
child: RotationTransition(
turns: _animation,
child: const Icon(Icons.arrow_drop_up, size: 180),
),
),
);
}
}
示例RotationTransition 非线性动画:
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
/// 会重复播放的控制器
late final AnimationController _repeatController;
/// 非线性动画
late final Animation<double> _curveAnimation;
@override
void initState() {
super.initState();
_repeatController = AnimationController(
duration: const Duration(seconds: 3),
vsync: this,
)..repeat();
/// Curves 存在多种模式,具体的效果查看Curves源码,有链接展示动画效果
_curveAnimation = CurvedAnimation(
parent: _repeatController,
curve: Curves.easeInCirc,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('RotationTransition——CurvedAnimation'),
),
body: Center(
child: RotationTransition(
turns: _curveAnimation,
child: const Icon(Icons.arrow_drop_up, size: 180),
),
),
);
}
}
-
自定义显式动画
- 使用AnimatedWidget
- 使用AnimatedBuilder(继承自AnimatedWidget)
示例AnimatedWidget:
import 'package:flutter/material.dart';
import 'dart:math';
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedWidget'),
),
body: const AnimatedIconWidgetPage(),
);
}
}
class AnimatedIconWidgetPage extends StatefulWidget {
const AnimatedIconWidgetPage({Key? key}) : super(key: key);
@override
State<AnimatedIconWidgetPage> createState() => _AnimatedIconWidgetPageState();
}
class _AnimatedIconWidgetPageState extends State<AnimatedIconWidgetPage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late CurvedAnimation _curveAnimation;
late Animation<double> _valueAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 5));
_curveAnimation = CurvedAnimation(parent: _controller, curve: Curves.bounceIn);
_valueAnimation = Tween(begin: 20.0, end: 300.0).animate(_curveAnimation);
_controller.forward();
}
@override
Widget build(BuildContext context) {
print("controller build");
return Center(
child: AnimatedIconWidget(
listenable: _valueAnimation,
),
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
class AnimatedIconWidget extends AnimatedWidget {
const AnimatedIconWidget({super.key, required Animation<double> listenable}) : super(listenable: listenable);
@override
Widget build(BuildContext context) {
print("widget build");
final anim = listenable as Animation<double>;
return FlutterLogo(size: anim.value);
}
}
示例AnimatedBuilder:
import 'package:flutter/material.dart';
import 'dart:math';
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedBuilder'),
),
body: const AnimatedIconWidgetPage(),
);
}
}
class AnimatedIconWidgetPage extends StatefulWidget {
const AnimatedIconWidgetPage({Key? key}) : super(key: key);
@override
State<AnimatedIconWidgetPage> createState() => _AnimatedIconWidgetPageState();
}
class _AnimatedIconWidgetPageState extends State<AnimatedIconWidgetPage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late CurvedAnimation _curveAnimation;
late Animation<double> _valueAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: const Duration(seconds: 5));
_curveAnimation = CurvedAnimation(parent: _controller, curve: Curves.bounceIn);
_valueAnimation = Tween(begin: 20.0, end: 300.0).animate(_curveAnimation);
_controller.forward();
}
@override
Widget build(BuildContext context) {
print("controller build");
return Center(
child: AnimatedBuilder(
animation: _valueAnimation,
builder: (_context,_child){
return SizedBox(
width: _valueAnimation.value,
height: _valueAnimation.value,
child: _child,
);
},
child: const FlutterLogo(),
),
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
-
CustomPainter
-
drawColor 绘制背景色
-
drawPoints 绘制点/线
-
drawLine 绘制线
-
drawArc 绘制弧/饼
-
drawRect 绘制矩形
-
drawRRect 绘制圆角矩形
-
drawDRRect 绘制嵌套矩形
-
drawCircle 绘制圆形
-
drawOval 绘制椭圆
-
drawPath 绘制路径
-
drawShadow 绘制阴影
-
drawImage 绘制图片
-
drawImageRect 绘制矩形图片
-
drawImageNine 绘制九图
-
drawParagraph 绘制文字段落
-
clipRect 裁剪矩形
-
clipRRect 裁剪圆角矩形
-
clipPath 裁剪由线围成区域
示例CustomPainter:
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({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<String> titles = [ "drawColor 绘制背景色", "drawPoints 绘制点/线", "drawLine 绘制线", "drawArc 绘制弧/饼", "drawRect 绘制矩形", "drawRRect 绘制圆角矩形", "drawDRRect 绘制嵌套矩形", "drawCircle 绘制圆形", "drawOval 绘制椭圆", "drawPath 绘制路径", "drawShadow 绘制阴影", "drawImage 绘制图片", "drawImageRect 绘制矩形图片", "drawImageNine 绘制九图", "drawParagraph 绘制文字段落", "clipRect 裁剪矩形", "clipRRect 裁剪圆角矩形", "clipPath 裁剪由线围成区域", ];
late ui.Image _uiImage;
@override
void initState() {
prepareImage();
super.initState();
}
void prepareImage() async {
Uint8List imageData = base64.decode(base64Strawberry.split(',').last);
ui.Codec codec = await ui.instantiateImageCodec(imageData);
ui.FrameInfo frameInfo = await codec.getNextFrame();
_uiImage = frameInfo.image;
}
Widget itemWidget(index) {
CustomPainter getPainter() {
final List<CustomPainter> painters = [ ColorPainter(), PointsPainter(), LinePainter(), ArcPainter(), RectPainter(), RRectPainter(), DRRectPainter(), CirclePainter(), OvalPainter(), PathPainter(), ShadowPainter(), ImagePainter(_uiImage), ImageRectPainter(_uiImage), ImageNinePainter(_uiImage), ParagraphPainter(), RectPainter(), RRectPainter(), PathPainter(), ];
return painters[index];
}
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (BuildContext context) {
return ColorCanvasPage(
painter: getPainter(),
);
},
),
);
},
child: Text(titles[index]),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('AnimatedBuilder'),
),
body: ListView.separated(
itemCount: titles.length,
separatorBuilder: (_, index) {
return const SizedBox(
height: 10,
);
},
itemBuilder: (_, index) {
return itemWidget(index);
}),
);
}
}
class ColorCanvasPage extends StatelessWidget {
final CustomPainter painter;
const ColorCanvasPage({Key? key, required this.painter}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(),
body: Center(
child: CustomPaint(
size: const Size(600, 600),
painter: painter,
),
));
}
}
class ColorPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawColor(Colors.orange, BlendMode.srcIn);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class PointsPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// 绘制点
canvas.drawPoints(ui.PointMode.points, [Offset(30.0, 30.0), Offset(60.0, 30.0), Offset(60.0, 60.0), Offset(30.0, 60.0)], Paint()..strokeWidth = 10.0);
canvas.drawPoints(
ui.PointMode.points,
[Offset(160.0, 30.0), Offset(190.0, 30.0), Offset(190.0, 60.0), Offset(160.0, 60.0)],
Paint()
..strokeWidth = 10.0
..strokeCap = StrokeCap.round);
// 绘制线
canvas.drawPoints(
ui.PointMode.lines,
[Offset(30.0, 100.0), Offset(60.0, 100.0), Offset(60.0, 130.0), Offset(30.0, 130.0)],
Paint()
..strokeWidth = 4.0
..strokeCap = StrokeCap.round);
// 绘制多边形
canvas.drawPoints(
ui.PointMode.polygon,
[Offset(160.0, 100.0), Offset(190.0, 100.0), Offset(190.0, 130.0), Offset(160.0, 130.0)],
Paint()
..strokeWidth = 4.0
..strokeCap = StrokeCap.round);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
class LinePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawLine(
Offset(30.0, 30.0),
Offset(200 - 30.0, 30.0),
Paint()
..strokeWidth = 10.0
..strokeCap = StrokeCap.butt);
canvas.drawLine(
Offset(30.0, 60.0),
Offset(200 - 30.0, 60.0),
Paint()
..strokeWidth = 10.0
..strokeCap = StrokeCap.round);
canvas.drawLine(
Offset(30.0, 90.0),
Offset(200 - 30.0, 90.0),
Paint()
..strokeWidth = 10.0
..strokeCap = StrokeCap.square);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ArcPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawArc(
Rect.fromCircle(center: Offset(50.0, 50.0), radius: 50.0),
0.0,
pi / 2,
false,
Paint()
..color = Colors.white
..strokeCap = StrokeCap.round
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
canvas.drawArc(
Rect.fromCircle(center: Offset(150.0, 50.0), radius: 50.0),
0.0,
pi / 2,
false,
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.fill);
canvas.drawArc(
Rect.fromCircle(center: Offset(300.0, 100.0), radius: 50.0),
-pi,
pi / 2,
true,
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
canvas.drawArc(
Rect.fromCircle(center: Offset(400.0, 100.0), radius: 50.0),
-pi,
pi / 3,
true,
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.fill);
canvas.drawArc(
Rect.fromLTWH(30.0, 150.0, 80.0, 50.0),
0.0,
pi * 2 * 2 / 3,
true,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
canvas.drawArc(
Rect.fromLTWH(150.0, 150.0, 80.0, 50.0),
0.0,
pi * 2 * 2 / 3,
true,
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
canvas.drawArc(
Rect.fromPoints(Offset(250.0, 150.0), Offset(300.0, 200.0)),
0.0,
5.0,
true,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
canvas.drawArc(
Rect.fromPoints(Offset(350.0, 150.0), Offset(400.0, 200.0)),
0.0,
5.0,
true,
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class RectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawRect(
Rect.fromPoints(Offset(30.0, 30.0), Offset(120.0, 60.0)),
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
canvas.drawRect(
Rect.fromPoints(Offset(160.0, 30.0), Offset(260.0, 80.0)),
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
canvas.drawRect(
Rect.fromLTRB(300.0, 30.0, 400.0, 50.0),
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
canvas.drawRect(
Rect.fromLTWH(30.0, 140.0, 100.0, 70.0),
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
canvas.drawRect(
Rect.fromCircle(center: Offset(200.0, 160.0), radius: 40.0),
Paint()
..color = Colors.white
..strokeWidth = 6.0
..style = PaintingStyle.stroke);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class RRectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// RRect.fromLTRBXY 方式
canvas.drawRRect(
RRect.fromLTRBXY(30.0, 30.0, 100.0, 80.0, 8.0, 8.0),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
canvas.drawRRect(
RRect.fromLTRBXY(120.0, 30.0, 220.0, 80.0, 8.0, 18.0),
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
// RRect.fromLTRBR 方式
canvas.drawRRect(
RRect.fromLTRBR(240.0, 30.0, 340.0, 80.0, Radius.circular(8.0)),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
// RRect.fromLTRBAndCorners 方式
canvas.drawRRect(
RRect.fromLTRBAndCorners(30.0, 120.0, 110.0, 160.0,
topLeft: Radius.circular(20.0), topRight: Radius.circular(20.0), bottomRight: Radius.circular(5.0), bottomLeft: Radius.circular(5.0)),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
// RRect.fromRectAndCorners 方式
canvas.drawRRect(
RRect.fromRectAndCorners(Rect.fromLTWH(120.0, 120.0, 100.0, 70.0),
topLeft: Radius.circular(20.0), topRight: Radius.circular(5.0), bottomRight: Radius.circular(5.0), bottomLeft: Radius.circular(20.0)),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
// RRect.fromRectAndRadius 方式
canvas.drawRRect(
RRect.fromRectAndRadius(Rect.fromLTWH(240.0, 120.0, 100.0, 80.0), Radius.elliptical(20.0, 18.0)),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
// RRect.fromRectXY 方式
canvas.drawRRect(
RRect.fromRectXY(Rect.fromCircle(center: Offset(70.0, 240.0), radius: 40.0), 8.0, 8.0),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class DRRectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawDRRect(
RRect.fromRectXY(Rect.fromCircle(center: Offset(90.0, 120.0), radius: 60.0), 8.0, 8.0),
RRect.fromRectXY(Rect.fromCircle(center: Offset(90.0, 120.0), radius: 50.0), 8.0, 8.0),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
canvas.drawDRRect(
RRect.fromRectXY(Rect.fromCircle(center: Offset(270.0, 120.0), radius: 60.0), 8.0, 8.0),
RRect.fromRectXY(Rect.fromCircle(center: Offset(270.0, 120.0), radius: 50.0), 8.0, 8.0),
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.fill);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class CirclePainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawCircle(
Offset(90.0, 120.0),
60.0,
Paint()
..color = Colors.white
..strokeWidth = 4.0
..style = PaintingStyle.stroke);
canvas.drawCircle(
Offset(270.0, 120.0),
60.0,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class OvalPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawOval(
Rect.fromLTRB(30.0, 30.0, 150.0, 80.0),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawOval(
Rect.fromLTRB(210.0, 30.0, 330.0, 80.0),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.fill);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class PathPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawPath(
Path()
..moveTo(30.0, 30.0)
..lineTo(80.0, 30.0)
..lineTo(80.0, 80.0)
..lineTo(30.0, 80.0)
..close(),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawPath(
Path()
..moveTo(100.0, 30.0)
..lineTo(100.0, 80.0)
..lineTo(150.0, 80.0)
..close(),
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
canvas.drawPath(
Path()
..moveTo(170.0, 30.0)
..lineTo(220.0, 30.0)
..lineTo(170.0, 80.0)
..lineTo(220.0, 80.0)
..close(),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawPath(
Path()
..moveTo(240.0, 80.0)
..lineTo(290.0, 80.0)
..lineTo(265.0, 30.0)
..close(),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawPath(
Path()
..moveTo(30.0, 120.0)
..lineTo(120.0, 120.0)
..lineTo(30.0, 160.0)
..lineTo(120.0, 160.0)
..addRect(Rect.fromLTWH(180.0, 130.0, 120.0, 70.0))
..addOval(Rect.fromLTWH(190.0, 140.0, 100.0, 50.0))
..moveTo(30.0, 230.0)
..lineTo(160.0, 230.0)
..close(),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawPath(
Path()..arcTo(Rect.fromCircle(center: Offset(70, 270), radius: 50), 0.0, pi, false),
Paint()
..color = Colors.white
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
canvas.drawPath(
Path()
..moveTo(210.0, 260.0)
..cubicTo(210.0, 330.0, 270.0, 300.0, 330.0, 250.0),
Paint()
..color = Colors.red
..strokeWidth = 3.0
..style = PaintingStyle.stroke);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ShadowPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
canvas.drawShadow(
Path()
..moveTo(30.0, 30.0)
..lineTo(120.0, 30.0)
..lineTo(120.0, 60.0)
..lineTo(30.0, 60.0)
..close(),
Colors.red,
4,
false);
canvas.drawShadow(
Path()
..moveTo(150.0, 30.0)
..lineTo(250.0, 30.0)
..lineTo(250.0, 60.0)
..lineTo(150.0, 60.0),
Colors.red,
10,
false);
canvas.drawShadow(
Path()
..moveTo(280.0, 30.0)
..lineTo(380.0, 30.0)
..lineTo(380.0, 60.0)
..lineTo(280.0, 60.0)
..close(),
Colors.red,
4,
true);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ImagePainter extends CustomPainter {
final ui.Image image;
ImagePainter(this.image);
@override
void paint(Canvas canvas, Size size) async {
print("paint size:$size imageSize:${Size(image.width * 1.0, image.height * 1.0)}");
canvas.drawColor(Colors.orange, BlendMode.srcIn);
canvas.drawImage(image, const Offset(0.0, 0.0), Paint());
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ImageRectPainter extends CustomPainter {
final ui.Image image;
ImageRectPainter(this.image);
@override
void paint(Canvas canvas, Size size) {
print("paint size:$size imageSize:${Size(image.width * 1.0, image.height * 1.0)}");
canvas.drawImageRect(image, const Rect.fromLTWH(0, 0, 200, 100), Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()), Paint());
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
// 还没有弄清楚原理
class ImageNinePainter extends CustomPainter {
final ui.Image image;
ImageNinePainter(this.image);
@override
void paint(Canvas canvas, Size size) {
print("paint size:$size imageSize:${Size(image.width * 1.0, image.height * 1.0)}");
canvas.drawImageNine(image, Rect.fromCenter(center: Offset(100,100),width: 200,height: 100), Rect.fromLTWH(0, 0, 400, 200), Paint());
//
canvas.drawImageRect(image, Rect.fromCenter(center: Offset(100,100),width: 200,height: 100), Rect.fromLTWH(300, 300, 200, 100), Paint());
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ParagraphPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(
textAlign: TextAlign.center,
fontWeight: FontWeight.w600,
fontStyle: FontStyle.normal,
fontSize: 18,
))
..pushStyle(ui.TextStyle(color: Colors.blue))
..addText("画十个姑娘陪着你");
ui.ParagraphConstraints pc = const ui.ParagraphConstraints(width: 320);
ui.Paragraph paragraph = pb.build()..layout(pc);
canvas.drawParagraph(paragraph, const Offset(30, 30));
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ClipRectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ClipRRectPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
class ClipPathPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
其中图片的Base64字符串可以在线生成:
地址:tool.chinaz.com/tools/imgto…
-
第三方动画库
-
lottie-flutter:
- 乐透动画,使用json数据来表示动画
pub.flutter-io.cn/packages/lo…
-
shimmer:
-
simple_animations:
-
animations:
- 官方动画示例,主要是页面跳转导航效果
- pub.flutter-io.cn/packages/an…
-
好玩的动画效果
-
文字闪烁效果
import 'package:flutter/material.dart';
main() {
runApp(const Myapp());
}
class Myapp extends StatelessWidget {
const Myapp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(home: ExamplePage());
}
}
class ExamplePage extends StatefulWidget {
const ExamplePage({Key? key}) : super(key: key);
@override
State<ExamplePage> createState() => _ExamplePageState();
}
class _ExamplePageState extends State<ExamplePage> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
int i = 0;
@override
void initState() {
_controller = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Scaffold(
backgroundColor: Colors.white,
body: GestureDetector(
onTap: (){
if(_controller.isAnimating){
_controller.stop();
}else{
_controller.repeat();
}
},
child: Center(
child: SizedBox(
width: size.width,
height: size.height,
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
double value = _animation.value;
Gradient gradient = LinearGradient(
colors: const [Colors.grey, Colors.white, Colors.grey],
stops: [value - 0.2, value, value + 0.2],
);
Shader shader = gradient.createShader(Rect.fromLTWH(0, 0, size.width, size.height));
return Column(
children: [
Text(
'文字闪烁效果',
style: TextStyle(
fontSize: 28.0,
foreground: Paint()..shader = shader,
// foreground: Paint()..color = Colors.red
),
),
const SizedBox(height: 20,),
Container(
width: size.width,
height: 40,
decoration: BoxDecoration(gradient:gradient ),
)
],
);
},
),
),
),
),
);
}
}