13

I want to create ripple animation using flutter. I already know ripple effect but this is not what I want , I want something which is here in the link

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
primo
  • 1,340
  • 3
  • 12
  • 40

2 Answers2

32

Output

enter image description here

  AnimationController _controller;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Title")),
      body: _buildBody(),
    );
  }

  Widget _buildBody() {
    return AnimatedBuilder(
      animation: CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn),
      builder: (context, child) {
        return Stack(
          alignment: Alignment.center,
          children: <Widget>[
            _buildContainer(150 * _controller.value),
            _buildContainer(200 * _controller.value),
            _buildContainer(250 * _controller.value),
            _buildContainer(300 * _controller.value),
            _buildContainer(350 * _controller.value),
            Align(child: Icon(Icons.phone_android, size: 44,)),
          ],
        );
      },
    );
  }

  Widget _buildContainer(double radius) {
    return Container(
      width: radius,
      height: radius,
      decoration: BoxDecoration(
        shape: BoxShape.circle,
        color: Colors.blue.withOpacity(1 - _controller.value),
      ),
    );
  }
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
  • I need one more help I want to align this in bottom center of whole layout. I am trying but not getting it – primo Apr 10 '19 at 12:24
  • 2
    Wrap `AnimatedBuilder` in `Align` and change `alignment` to `Alignment.bottomCenter` – CopsOnRoad Apr 10 '19 at 12:28
  • 6
    `with TickerProviderStateMixin ` Add this mixin to your state class, if you got error on vsync – Devinder Jhinjer May 20 '20 at 12:19
  • How can I add this animation on google map's current location(flutter)? – Rohit Luthra May 20 '20 at 13:41
  • @RohitLuthra Last time when I used Google maps, it wasn't supporting flutter regular widget to be displayed as marker, and there was an issue on Github, so not sure if you can display the widgets today. – CopsOnRoad May 21 '20 at 00:33
  • `CurvedAnimation` in your code does nothing. `AnimatedBuilder` API is broken as it does not pass `animation` controller to `builder` function. – mip Sep 13 '21 at 21:44
13

Here is another version using CustomPaint

import 'dart:math' as math show sin, pi, sqrt;

import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';

class Ripples extends StatefulWidget {
  const Ripples({
    Key key,
    this.size = 80.0,
    this.color = Colors.pink,
    this.onPressed,
    @required this.child,
  }) : super(key: key);

  final double size;
  final Color color;
  final Widget child;
  final VoidCallback onPressed;

  @override
  _RipplesState createState() => _RipplesState();
}

class _CirclePainter extends CustomPainter {
  _CirclePainter(
    this._animation, {
    @required this.color,
  }) : super(repaint: _animation);

  final Color color;
  final Animation<double> _animation;

  void circle(Canvas canvas, Rect rect, double value) {
    final double opacity = (1.0 - (value / 4.0)).clamp(0.0, 1.0);
    final Color _color = color.withOpacity(opacity);

    final double size = rect.width / 2;
    final double area = size * size;
    final double radius = math.sqrt(area * value / 4);

    final Paint paint = Paint()..color = _color;
    canvas.drawCircle(rect.center, radius, paint);
  }

  @override
  void paint(Canvas canvas, Size size) {
    final Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);

    for (int wave = 3; wave >= 0; wave--) {
      circle(canvas, rect, wave + _animation.value);
    }
  }

  @override
  bool shouldRepaint(_CirclePainter oldDelegate) => true;
}

class _RipplesState extends State<Ripples> with TickerProviderStateMixin {
  AnimationController _controller;

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

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Widget _button() {
    return Center(
      child: ClipRRect(
        borderRadius: BorderRadius.circular(widget.size),
        child: DecoratedBox(
          decoration: BoxDecoration(
            gradient: RadialGradient(
              colors: <Color>[
                widget.color,
                Color.lerp(widget.color, Colors.black, .05)
              ],
            ),
          ),
          child: ScaleTransition(
            scale: Tween(begin: 0.95, end: 1.0).animate(
              CurvedAnimation(
                parent: _controller,
                curve: const _PulsateCurve(),
              ),
            ),
            child: widget.child,
          ),
        ),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      painter: _CirclePainter(
        _controller,
        color: widget.color,
      ),
      child: SizedBox(
        width: widget.size * 2.125,
        height: widget.size * 2.125,
        child: _button(),
      ),
    );
  }
}

class _PulsateCurve extends Curve {
  const _PulsateCurve();

  @override
  double transform(double t) {
    if (t == 0 || t == 1) {
      return 0.01;
    }
    return math.sin(t * math.pi);
  }
}
jogboms
  • 573
  • 4
  • 11
  • 1
    lordy... so much boilerplate for a simple thing! – U Avalos May 21 '19 at 03:43
  • 4
    Believe it or not, this is more performant than the accepted solution. CustomPainter is verbose, but performant AF, and even this chunk is better than java solutions in which you should install a library only for this – RegularGuy Sep 19 '20 at 20:44