Flutter动画高级技巧:创建流畅的用户体验
Flutter动画高级技巧:创建流畅的用户体验
引言
动画是现代移动应用中不可或缺的一部分,它可以提升用户体验,使应用更加生动和富有吸引力。Flutter提供了强大的动画系统,从基本的补间动画到复杂的物理动画,都可以轻松实现。本文将深入探讨Flutter动画的高级技巧,帮助你创建流畅、自然的动画效果。
一、Flutter动画基础
1. 动画控制器
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
class MyAnimationWidget extends StatefulWidget {
@override
_MyAnimationWidgetState createState() => _MyAnimationWidgetState();
}
class _MyAnimationWidgetState extends State<MyAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = CurvedAnimation(
parent: _controller,
curve: Curves.easeInOut,
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ScaleTransition(
scale: _animation,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
),
);
}
}
2. 动画类型
Flutter提供了多种动画类型:
- 补间动画:在两个值之间进行插值
- 物理动画:模拟物理世界的运动
- 页面转场动画:页面之间的过渡效果
- Hero动画:元素在页面之间的平滑过渡
二、高级补间动画
1. 自定义曲线
// 自定义曲线
class CustomCurve extends Curve {
@override
double transform(double t) {
// 自定义曲线逻辑
return t * t * (3.0 - 2.0 * t); // 三次贝塞尔曲线
}
}
// 使用自定义曲线
_animation = CurvedAnimation(
parent: _controller,
curve: CustomCurve(),
);
2. 多动画组合
class MultiAnimationWidget extends StatefulWidget {
@override
_MultiAnimationWidgetState createState() => _MultiAnimationWidgetState();
}
class _MultiAnimationWidgetState extends State<MultiAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
late Animation<double> _opacityAnimation;
late Animation<Offset> _slideAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 0.5, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeIn),
);
_slideAnimation = Tween<Offset>(begin: Offset(0, 1), end: Offset(0, 0)).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FadeTransition(
opacity: _opacityAnimation,
child: SlideTransition(
position: _slideAnimation,
child: ScaleTransition(
scale: _scaleAnimation,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
),
),
),
);
}
}
3. 动画序列
class AnimationSequenceWidget extends StatefulWidget {
@override
_AnimationSequenceWidgetState createState() => _AnimationSequenceWidgetState();
}
class _AnimationSequenceWidgetState extends State<AnimationSequenceWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _firstAnimation;
late Animation<double> _secondAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 4),
vsync: this,
);
_firstAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.0, 0.5, curve: Curves.easeOut),
),
);
_secondAnimation = Tween<double>(begin: 1.0, end: 0.5).animate(
CurvedAnimation(
parent: _controller,
curve: Interval(0.5, 1.0, curve: Curves.easeIn),
),
);
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.scale(
scale: _firstAnimation.value > 0.5 ? _secondAnimation.value : _firstAnimation.value,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
);
},
),
),
);
}
}
三、物理动画
1. 弹簧动画
import 'package:flutter/physics.dart';
class SpringAnimationWidget extends StatefulWidget {
@override
_SpringAnimationWidgetState createState() => _SpringAnimationWidgetState();
}
class _SpringAnimationWidgetState extends State<SpringAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
final spring = SpringSimulation(
SpringDescription(
mass: 1.0,
stiffness: 100.0,
damping: 10.0,
),
0.0,
1.0,
0.0,
);
_animation = Tween<Offset>(begin: Offset(0, 0), end: Offset(1, 0)).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.linear,
),
);
_controller.animateWith(spring);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SlideTransition(
position: _animation,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
),
),
);
}
}
2. 重力动画
class GravityAnimationWidget extends StatefulWidget {
@override
_GravityAnimationWidgetState createState() => _GravityAnimationWidgetState();
}
class _GravityAnimationWidgetState extends State<GravityAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
final gravity = GravitySimulation(
9.8, // 重力加速度
0.0, // 初始位置
0.0, // 初始速度
500.0, // 目标位置
);
_animation = Tween<double>(begin: 0.0, end: 500.0).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.linear,
),
);
_controller.animateWith(gravity);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: [
AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Positioned(
left: 100,
top: _animation.value,
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
},
),
],
),
);
}
}
四、Hero动画
1. 基本Hero动画
// 源页面
class SourcePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Source Page')),
body: GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => DestinationPage()),
);
},
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://example.com/image.jpg',
width: 100,
height: 100,
),
),
),
);
}
}
// 目标页面
class DestinationPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Destination Page')),
body: Center(
child: Hero(
tag: 'imageHero',
child: Image.network(
'https://example.com/image.jpg',
width: 300,
height: 300,
),
),
),
);
}
}
2. 高级Hero动画
class AdvancedHeroWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Advanced Hero')),
body: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
itemCount: 6,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailPage(imageIndex: index),
),
);
},
child: Hero(
tag: 'image_$index',
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
image: DecorationImage(
image: NetworkImage('https://example.com/image$index.jpg'),
fit: BoxFit.cover,
),
),
),
),
);
},
),
);
}
}
class DetailPage extends StatelessWidget {
final int imageIndex;
const DetailPage({Key? key, required this.imageIndex}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
color: Colors.black,
child: Center(
child: Hero(
tag: 'image_$imageIndex',
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.7,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage('https://example.com/image$imageIndex.jpg'),
fit: BoxFit.contain,
),
),
),
),
),
),
),
);
}
}
五、页面转场动画
1. 自定义页面转场
class CustomTransitionPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Custom Transition')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => SecondPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.easeInOut;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
transitionDuration: Duration(milliseconds: 500),
),
);
},
child: Text('Go to Second Page'),
),
),
);
}
}
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Page')),
body: Center(
child: Text('Second Page Content'),
),
);
}
}
2. 淡入淡出转场
Navigator.push(
context,
PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) => SecondPage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
),
);
六、动画控制器高级用法
1. 动画状态监听
class AnimationListenerWidget extends StatefulWidget {
@override
_AnimationListenerWidgetState createState() => _AnimationListenerWidgetState();
}
class _AnimationListenerWidgetState extends State<AnimationListenerWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
String _status = 'Ready';
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.addStatusListener((status) {
setState(() {
switch (status) {
case AnimationStatus.dismissed:
_status = 'Dismissed';
break;
case AnimationStatus.forward:
_status = 'Forward';
break;
case AnimationStatus.reverse:
_status = 'Reverse';
break;
case AnimationStatus.completed:
_status = 'Completed';
break;
}
});
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.scale(
scale: _animation.value,
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
);
},
),
SizedBox(height: 20),
Text('Animation Status: $_status'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
if (_controller.status == AnimationStatus.completed) {
_controller.reverse();
} else {
_controller.forward();
}
},
child: Text('Toggle Animation'),
),
],
),
),
);
}
}
2. 动画值监听
class AnimationValueListenerWidget extends StatefulWidget {
@override
_AnimationValueListenerWidgetState createState() => _AnimationValueListenerWidgetState();
}
class _AnimationValueListenerWidgetState extends State<AnimationValueListenerWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
double _currentValue = 0.0;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_animation.addListener(() {
setState(() {
_currentValue = _animation.value;
});
});
_controller.forward();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: 200 * _animation.value,
height: 200 * _animation.value,
color: Colors.blue,
);
},
),
SizedBox(height: 20),
Text('Animation Value: ${_currentValue.toStringAsFixed(2)}'),
],
),
),
);
}
}
七、性能优化
1. 使用AnimatedBuilder
// 不好的做法:整个widget重建
class BadAnimationWidget extends StatefulWidget {
@override
_BadAnimationWidgetState createState() => _BadAnimationWidgetState();
}
class _BadAnimationWidgetState extends State<BadAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
transform: Matrix4.rotationZ(_animation.value * 2 * 3.14159),
child: ExpensiveWidget(), // 每次动画都会重建
),
),
);
}
}
// 好的做法:只重建动画部分
class GoodAnimationWidget extends StatefulWidget {
@override
_GoodAnimationWidgetState createState() => _GoodAnimationWidgetState();
}
class _GoodAnimationWidgetState extends State<GoodAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.rotate(
angle: _animation.value * 2 * 3.14159,
child: child, // 不会重建
);
},
child: ExpensiveWidget(), // 只构建一次
),
),
);
}
}
2. 使用RepaintBoundary
class RepaintBoundaryWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RepaintBoundary(
child: AnimatedWidget(), // 隔离重绘
),
),
);
}
}
3. 避免在动画中使用setState
// 不好的做法:在动画监听器中使用setState
_animation.addListener(() {
setState(() {
// 更新状态
});
});
// 好的做法:使用AnimatedBuilder
AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
// 使用动画值
);
},
);
八、实战案例:复杂动画效果
1. 加载动画
class LoadingAnimationWidget extends StatefulWidget {
@override
_LoadingAnimationWidgetState createState() => _LoadingAnimationWidgetState();
}
class _LoadingAnimationWidgetState extends State<LoadingAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: _controller, curve: Curves.linear),
);
_controller.repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.rotate(
angle: _animation.value * 2 * 3.14159,
child: Container(
width: 50,
height: 50,
child: CircularProgressIndicator(),
),
);
},
),
),
);
}
}
2. 脉冲动画
class PulseAnimationWidget extends StatefulWidget {
@override
_PulseAnimationWidgetState createState() => _PulseAnimationWidgetState();
}
class _PulseAnimationWidgetState extends State<PulseAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
_animation = Tween<double>(begin: 1.0, end: 1.2).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
_controller.repeat(reverse: true);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Transform.scale(
scale: _animation.value,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(50),
),
),
);
},
),
),
);
}
}
3. 复杂交互动画
class InteractiveAnimationWidget extends StatefulWidget {
@override
_InteractiveAnimationWidgetState createState() => _InteractiveAnimationWidgetState();
}
class _InteractiveAnimationWidgetState extends State<InteractiveAnimationWidget> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<Offset> _animation;
bool _isDragging = false;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_animation = Tween<Offset>(begin: Offset(0, 0), end: Offset(1, 0)).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onPanStart: (details) {
setState(() => _isDragging = true);
},
onPanUpdate: (details) {
final width = MediaQuery.of(context).size.width;
final progress = (details.localPosition.dx / width).clamp(0.0, 1.0);
_controller.value = progress;
},
onPanEnd: (details) {
setState(() => _isDragging = false);
if (_controller.value > 0.5) {
_controller.forward();
} else {
_controller.reverse();
}
},
child: Container(
width: 300,
height: 60,
decoration: BoxDecoration(
color: Colors.grey[200],
borderRadius: BorderRadius.circular(30),
),
child: Stack(
children: [
SlideTransition(
position: _animation,
child: Container(
width: 150,
height: 60,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(30),
),
),
),
Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'Off',
style: TextStyle(
color: _controller.value > 0.5 ? Colors.grey : Colors.white,
fontWeight: _controller.value > 0.5 ? FontWeight.normal : FontWeight.bold,
),
),
Text(
'On',
style: TextStyle(
color: _controller.value > 0.5 ? Colors.white : Colors.grey,
fontWeight: _controller.value > 0.5 ? FontWeight.bold : FontWeight.normal,
),
),
],
),
),
],
),
),
),
),
);
}
}
九、第三方动画库
1. 使用flutter_animate
// 添加依赖
dependencies:
flutter_animate: ^4.0.0
// 使用示例
import 'package:flutter_animate/flutter_animate.dart';
class AnimateWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Container(
width: 200,
height: 200,
color: Colors.blue,
)
.animate()
.fadeIn(duration: 1000.ms)
.scale(duration: 500.ms)
.moveX(amount: 100, duration: 1000.ms)
.rotate(duration: 1000.ms),
),
);
}
}
2. 使用 rive
// 添加依赖
dependencies:
rive: ^0.11.0
// 使用示例
import 'package:rive/rive.dart';
class RiveAnimationWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RiveAnimation.asset(
'assets/animation.riv',
animations: ['idle', 'hover'],
),
),
);
}
}
十、总结
Flutter的动画系统非常强大,通过本文介绍的高级技巧,你可以:
- 掌握动画控制器:创建和管理动画控制器
- 实现高级补间动画:自定义曲线、多动画组合、动画序列
- 使用物理动画:弹簧动画、重力动画
- 创建Hero动画:实现元素在页面间的平滑过渡
- 自定义页面转场:创建独特的页面切换效果
- 优化动画性能:使用AnimatedBuilder、RepaintBoundary
- 使用第三方库:flutter_animate、rive等
通过综合应用这些技巧,你将能够创建出流畅、自然的动画效果,提升应用的用户体验和视觉吸引力。
输入输出示例
输入输出示例
场景:创建一个带有动画效果的登录按钮
代码:
class AnimatedLoginButton extends StatefulWidget {
@override
_AnimatedLoginButtonState createState() => _AnimatedLoginButtonState();
}
class _AnimatedLoginButtonState extends State<AnimatedLoginButton> with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
late Animation<double> _opacityAnimation;
bool _isLoading = false;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 300),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 1.0, end: 0.9).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
_opacityAnimation = Tween<double>(begin: 1.0, end: 0.7).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Future<void> _login() async {
setState(() => _isLoading = true);
_controller.forward();
// 模拟登录过程
await Future.delayed(Duration(2000));
_controller.reverse();
setState(() => _isLoading = false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: _isLoading ? null : _login,
onTapDown: (_) => _controller.forward(),
onTapUp: (_) => _controller.reverse(),
onTapCancel: () => _controller.reverse(),
child: FadeTransition(
opacity: _opacityAnimation,
child: ScaleTransition(
scale: _scaleAnimation,
child: Container(
width: 200,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(25),
),
child: Center(
child: _isLoading
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: Text(
'Login',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
),
),
),
),
),
),
);
}
}
输出:一个带有按下动画和加载状态的登录按钮,按下时会缩小并降低透明度,加载时显示圆形进度指示器。
代码优化建议
-
使用Animation而非AnimationController直接控制:AnimationController负责动画的控制,Animation负责动画值的计算
-
合理使用Curve:选择合适的曲线可以使动画更加自然
-
使用AnimatedBuilder:避免不必要的widget重建
-
使用RepaintBoundary:隔离动画重绘区域,提高性能
-
避免在动画监听器中使用setState:使用AnimatedBuilder代替
-
合理设置动画 duration:根据动画类型和场景设置合适的持续时间
-
使用物理动画:对于模拟物理效果的动画,使用SpringSimulation等物理模拟
-
优化Hero动画:确保源和目标Hero的tag一致,避免冲突
-
使用第三方库:对于复杂动画,考虑使用flutter_animate等库
-
测试不同设备:在不同性能的设备上测试动画,确保流畅运行
通过遵循这些最佳实践,你将能够创建出更加流畅、自然的动画效果,提升应用的用户体验。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)