Building Custom Animations in Flutter

This building custom animations in flutter is posted by seven.srikanth at 5/2/2025 11:40:55 PM



<h1 id="building-custom-animations-in-flutter">Building Custom Animations in Flutter</h1> <p>Flutter provides powerful tools for creating custom animations. This article will guide you through building custom animations using AnimationController, Tween, and custom painters.</p> <h2 id="understanding-animation-basics">Understanding Animation Basics</h2> <h3 id="animationcontroller">1. AnimationController</h3> <pre>class CustomAnimation extends StatefulWidget { @override _CustomAnimationState createState() =&gt; _CustomAnimationState(); }

class _CustomAnimationState extends State&lt;CustomAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, )..repeat(); }

@override void dispose() { _controller.dispose(); super.dispose(); } } </pre> <h3 id="tween-and-curvedanimation">2. Tween and CurvedAnimation</h3> <pre>class _CustomAnimationState extends State&lt;CustomAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _animation;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, );

_animation = Tween&amp;lt;double&amp;gt;(
  begin: 0.0,
  end: 1.0,
).animate(CurvedAnimation(
  parent: _controller,
  curve: Curves.easeInOut,
));

_controller.repeat();

} } </pre> <h2 id="creating-custom-animations">Creating Custom Animations</h2> <h3 id="custom-painter-animation">1. Custom Painter Animation</h3> <pre>class CustomPainterAnimation extends CustomPainter { final Animation&lt;double&gt; animation;

CustomPainterAnimation(this.animation) : super(repaint: animation);

@override void paint(Canvas canvas, Size size) { final paint = Paint() ..color = Colors.blue ..style = PaintingStyle.fill;

final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 4 * animation.value;

canvas.drawCircle(center, radius, paint);

}

@override bool shouldRepaint(CustomPainterAnimation oldDelegate) { return oldDelegate.animation != animation; } } </pre> <h3 id="using-custom-painter">2. Using Custom Painter</h3> <pre>class AnimatedCircle extends StatelessWidget { @override Widget build(BuildContext context) { return CustomPaint( painter: CustomPainterAnimation( Tween&lt;double&gt;(begin: 0, end: 1).animate( AnimationController( duration: Duration(seconds: 2), vsync: this, )..repeat(), ), ), child: Container(), ); } } </pre> <h2 id="advanced-animation-techniques">Advanced Animation Techniques</h2> <h3 id="staggered-animations">1. Staggered Animations</h3> <pre>class StaggeredAnimation extends StatefulWidget { @override _StaggeredAnimationState createState() =&gt; _StaggeredAnimationState(); }

class _StaggeredAnimationState extends State&lt;StaggeredAnimation&gt; with TickerProviderStateMixin { late AnimationController _controller; late List&lt;Animation&lt;double&gt;&gt; _animations;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, );

_animations = List.generate(3, (index) {
  return Tween&amp;lt;double&amp;gt;(
    begin: 0.0,
    end: 1.0,
  ).animate(CurvedAnimation(
    parent: _controller,
    curve: Interval(
      index * 0.3,
      (index + 1) * 0.3,
      curve: Curves.easeInOut,
    ),
  ));
});

_controller.forward();

} } </pre> <h3 id="physics-based-animations">2. Physics-Based Animations</h3> <pre>class PhysicsAnimation extends StatefulWidget { @override _PhysicsAnimationState createState() =&gt; _PhysicsAnimationState(); }

class _PhysicsAnimationState extends State&lt;PhysicsAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _animation;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, );

_animation = Tween&amp;lt;double&amp;gt;(
  begin: 0.0,
  end: 1.0,
).animate(CurvedAnimation(
  parent: _controller,
  curve: Curves.bounceOut,
));

_controller.forward();

} } </pre> <h2 id="performance-optimization">Performance Optimization</h2> <h3 id="using-repaintboundary">1. Using RepaintBoundary</h3> <pre>RepaintBoundary( child: CustomPaint( painter: CustomPainterAnimation(_animation), child: Container(), ), ) </pre> <h3 id="optimizing-animation-updates">2. Optimizing Animation Updates</h3> <pre>class OptimizedAnimation extends StatefulWidget { @override _OptimizedAnimationState createState() =&gt; _OptimizedAnimationState(); }

class _OptimizedAnimationState extends State&lt;OptimizedAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _animation;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, );

_animation = Tween&amp;lt;double&amp;gt;(
  begin: 0.0,
  end: 1.0,
).animate(_controller);

_controller.addListener(() {
  if (_controller.isCompleted) {
    _controller.reverse();
  } else if (_controller.isDismissed) {
    _controller.forward();
  }
});

_controller.forward();

} } </pre> <h2 id="best-practices">Best Practices</h2> <ol> <li><strong>Use Appropriate Curves</strong>: Choose curves that match the animation's purpose</li> <li><strong>Optimize Performance</strong>: Use RepaintBoundary for complex animations</li> <li><strong>Handle Disposal</strong>: Always dispose of AnimationControllers</li> <li><strong>Test on Multiple Devices</strong>: Ensure smooth performance across devices</li> <li><strong>Consider Accessibility</strong>: Provide alternative content for users who prefer reduced motion</li> </ol> <h2 id="example-custom-loading-animation">Example: Custom Loading Animation</h2> <pre>class CustomLoadingAnimation extends StatefulWidget { @override _CustomLoadingAnimationState createState() =&gt; _CustomLoadingAnimationState(); }

class _CustomLoadingAnimationState extends State&lt;CustomLoadingAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _rotationAnimation; late Animation&lt;double&gt; _scaleAnimation;

@override void initState() { super.initState(); _controller = AnimationController( duration: Duration(seconds: 2), vsync: this, );

_rotationAnimation = Tween&amp;lt;double&amp;gt;(
  begin: 0.0,
  end: 2 * pi,
).animate(CurvedAnimation(
  parent: _controller,
  curve: Curves.linear,
));

_scaleAnimation = Tween&amp;lt;double&amp;gt;(
  begin: 0.5,
  end: 1.0,
).animate(CurvedAnimation(
  parent: _controller,
  curve: Curves.easeInOut,
));

_controller.repeat();

}

@override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, child) { return Transform.rotate( angle: _rotationAnimation.value, child: Transform.scale( scale: _scaleAnimation.value, child: Icon(Icons.refresh, size: 48), ), ); }, ); }

@override void dispose() { _controller.dispose(); super.dispose(); } } </pre> <h2 id="conclusion">Conclusion</h2> <p>Building custom animations in Flutter involves:</p> <ul> <li>Understanding AnimationController and Tween</li> <li>Creating custom painters for complex animations</li> <li>Implementing staggered and physics-based animations</li> <li>Optimizing performance with RepaintBoundary</li> <li>Following best practices for smooth animations</li> </ul> <ol> <li><p><strong>Performance</strong></p> <ul> <li>Use <code>const</code> widgets where possible</li> <li>Avoid unnecessary rebuilds</li> <li>Use <code>RepaintBoundary</code> for complex animations</li> <li>Optimize animation curves</li> </ul> </li> <li><p><strong>User Experience</strong></p> <ul> <li>Keep animations short and purposeful</li> <li>Provide clear feedback</li> <li>Consider motion sickness</li> <li>Respect user preferences</li> </ul> </li> <li><p><strong>Code Organization</strong></p> <ul> <li>Separate animation logic</li> <li>Use mixins for common behaviors</li> <li>Implement proper cleanup</li> <li>Document animation parameters</li> </ul> </li> </ol> <h2 id="common-animation-patterns">Common Animation Patterns</h2> <h3 id="page-transitions">1. Page Transitions</h3> <pre>class CustomPageRoute extends PageRouteBuilder { final Widget page;

CustomPageRoute() : super( pageBuilder: (context, animation, secondaryAnimation) =&gt; page, transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: child, ); }, ); }

// Usage Navigator.push( context, CustomPageRoute(page: const NextPage()), ); </pre> <h3 id="loading-animations">2. Loading Animations</h3> <pre>class LoadingAnimation extends StatefulWidget { const LoadingAnimation();

@override State&lt;LoadingAnimation&gt; createState() =&gt; _LoadingAnimationState(); }

class _LoadingAnimationState extends State&lt;LoadingAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _rotationAnimation;

@override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(seconds: 2), );

_rotationAnimation = Tween&amp;lt;double&amp;gt;(
  begin: 0,
  end: 2 * math.pi,
).animate(_controller);

_controller.repeat();

}

@override Widget build(BuildContext context) { return RotationTransition( turns: _rotationAnimation, child: const Icon(Icons.refresh, size: 50), ); } } </pre> <h3 id="interactive-animations">3. Interactive Animations</h3> <pre>class InteractiveAnimation extends StatefulWidget { const InteractiveAnimation();

@override State&lt;InteractiveAnimation&gt; createState() =&gt; _InteractiveAnimationState(); }

class _InteractiveAnimationState extends State&lt;InteractiveAnimation&gt; with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation&lt;double&gt; _animation; double _dragPosition = 0;

@override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), );

_animation = Tween&amp;lt;double&amp;gt;(begin: 0, end: 1).animate(_controller);

}

@override Widget build(BuildContext context) { return GestureDetector( onPanUpdate: (details) { setState(() { dragPosition += details.delta.dx; }); }, onPanEnd: () { if (_dragPosition &gt; 100) { _controller.forward(); } else { _controller.reverse(); } }, child: Transform.translate( offset: Offset(_dragPosition, 0), child: const FlutterLogo(size: 100), ), ); } } </pre> <h2 id="testing-animations">Testing Animations</h2> <pre>void main() { group(&#39;Animation Tests&#39;, () { testWidgets(&#39;animates correctly&#39;, (tester) async { await tester.pumpWidget(const MaterialApp( home: BasicAnimation(), ));

  expect(find.byType(FlutterLogo), findsOneWidget);

  await tester.pump(const Duration(seconds: 1));
  // Verify animation state
});

testWidgets(&amp;#39;handles user interaction&amp;#39;, (tester) async {
  await tester.pumpWidget(const MaterialApp(
    home: InteractiveAnimation(),
  ));

  await tester.drag(find.byType(FlutterLogo), const Offset(200, 0));
  await tester.pumpAndSettle();

  // Verify final position
});

}); } </pre> <h2 id="performance-optimization-1">Performance Optimization</h2> <ol> <li><p><strong>Use RepaintBoundary</strong></p> <pre>RepaintBoundary( child: AnimatedContainer( duration: const Duration(seconds: 1), color: Colors.blue, ), ) </pre> </li> <li><p><strong>Optimize Animation Curves</strong></p> <pre>CurvedAnimation( parent: _controller, curve: Curves.easeOutCubic, // More performant than easeOut ) </pre> </li> <li><p><strong>Use Transform Instead of Layout Changes</strong></p> <pre>Transform.scale( scale: _animation.value, child: const FlutterLogo(), ) </pre> </li> </ol> <h2 id="conclusion-1">Conclusion</h2> <p>Creating custom animations in Flutter requires understanding of:</p> <ol> <li><strong>Animation Fundamentals</strong>: Controllers, tweens, and curves</li> <li><strong>Performance Considerations</strong>: Optimization and best practices</li> <li><strong>User Experience</strong>: Purposeful and smooth animations</li> <li><strong>Advanced Techniques</strong>: Physics, staggered, and custom animations</li> <li><strong>Testing</strong>: Verifying animation behavior</li> </ol> <p>Remember to:</p> <ul> <li>Keep animations purposeful and smooth</li> <li>Optimize for performance</li> <li>Test thoroughly</li> <li>Consider accessibility</li> <li>Document animation parameters</li> </ul> <p>By following these guidelines, you can create engaging and performant animations that enhance your Flutter applications.</p>


Tags: flutter,markdown,generated








0 Comments
Login to comment.
Recent Comments