Sparkling animation in flutter

466

Solution 1

I would suggest a custom paint approche. My awswer is highly customisable. I only change the innerOuterRadiusRatio and the velocity. You can change the color or Opacity, the number of edges of the star, the rotation(angleOffsetToCenterStar),and the beamLength.

import 'package:flutter/material.dart';
import 'dart:math' as math;

class Sparkling extends StatefulWidget {
  const Sparkling({Key? key}) : super(key: key);

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

class _SparklingState extends State<Sparkling>
    with SingleTickerProviderStateMixin {
  late AnimationController animationController;
  late Animation animation;
  late List<MyStar> myStars;

  @override
  void initState() {
    super.initState();

    myStars = <MyStar>[];
    animationController = AnimationController(
        vsync: this,
        duration: const Duration(
          milliseconds: 250,
        ));
    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        for (final star in myStars) {
          star.isEnable = math.Random().nextBool();
        }

        animationController.forward();
      }
    });
    animation = Tween<double>(begin: 0, end: 8).animate(CurvedAnimation(
        parent: animationController, curve: Curves.easeInOutSine));
    animation.addListener(() {
      setState(() {});
    });

    animationController.forward();
  }

  void postFrameCallback(_) {
    if (!mounted) {
      return;
    }
    final size = MediaQuery.of(context).size;
    if (myStars.isEmpty) {
      myStars = List.generate(60, (index) {
        double velocityX = 2 * (math.Random().nextDouble());//max velocity 2
        double velocityY = 2 * (math.Random().nextDouble());

        velocityX = math.Random().nextBool() ? velocityX : -velocityX;
        velocityY = math.Random().nextBool() ? velocityY : -velocityY;

        return MyStar(
            isEnable: math.Random().nextBool(),
            innerCirclePoints: 4,
            beamLength: math.Random().nextDouble() * (8 - 2) + 2,
            innerOuterRadiusRatio: 0.0,
            angleOffsetToCenterStar: 0,
            center: Offset(size.width * (math.Random().nextDouble()),
                size.height * (math.Random().nextDouble())),
            velocity: Offset(velocityX, velocityY),
            color: Colors.white);
      });
    } else {
      for (final star in myStars) {
        star.center = star.center + star.velocity;
        if (star.isEnable) {
          star.innerOuterRadiusRatio = animation.value;

          if (star.center.dx >= size.width) {
            if (star.velocity.dy > 0) {
              star.velocity = const Offset(-1, 1);
            } else {
              star.velocity = const Offset(-1, -1);
            }

            star.center = Offset(size.width, star.center.dy);
          } else if (star.center.dx <= 0) {
            if (star.velocity.dy > 0) {
              star.velocity = const Offset(1, 1);
            } else {
              star.velocity = const Offset(1, -1);
            }

            star.center = Offset(0, star.center.dy);
          } else if (star.center.dy >= size.height) {
            if (star.velocity.dx > 0) {
              star.velocity = const Offset(1, -1);
            } else {
              star.velocity = const Offset(-1, -1);
            }

            star.center = Offset(star.center.dx, size.height);
          } else if (star.center.dy <= 0) {
            if (star.velocity.dx > 0) {
              star.velocity = const Offset(1, 1);
            } else {
              star.velocity = const Offset(-1, 1);
            }

            star.center = Offset(star.center.dx, 0);
          }
        }
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance!.addPostFrameCallback(postFrameCallback);

    return CustomPaint(
        size: MediaQuery.of(context).size,
        painter: StarPainter(
          myStars: myStars,
        ));
  }
}

The CustomPainter

class StarPainter extends CustomPainter {
  List<MyStar> myStars;

  StarPainter({required this.myStars});

  List<Map> calcStarPoints(
      {required double centerX,
      required double centerY,
      required int innerCirclePoints,
      required double innerRadius,
      required double outerRadius,
      required double angleOffsetToCenterStar}) {
    final angle = ((math.pi) / innerCirclePoints);

    final totalPoints = innerCirclePoints * 2; // 10 in a 5-points star
    List<Map> points = [];
    for (int i = 0; i < totalPoints; i++) {
      bool isEvenIndex = i % 2 == 0;
      final r = isEvenIndex ? outerRadius : innerRadius;

      var currY = centerY + math.cos(i * angle + angleOffsetToCenterStar) * r;
      var currX = centerX + math.sin(i * angle + angleOffsetToCenterStar) * r;
      points.add({'x': currX, 'y': currY});
    }
    return points;
  }

  @override
  void paint(Canvas canvas, Size size) {
    for (final myStar in myStars) {
      final innerRadius = myStar.beamLength / myStar.innerCirclePoints;
      final outerRadius = innerRadius * myStar.innerOuterRadiusRatio;

      List<Map> points = calcStarPoints(
          centerX: myStar.center.dx,
          centerY: myStar.center.dy,
          innerCirclePoints: myStar.innerCirclePoints,
          innerRadius: innerRadius,
          outerRadius: outerRadius,
          angleOffsetToCenterStar: myStar.angleOffsetToCenterStar);
      var star = Path()..moveTo(points[0]['x'], points[0]['y']);
      for (var point in points) {
        star.lineTo(point['x'], point['y']);
      }

      canvas.drawPath(
        star, 
        Paint()..color = myStar.color,
      );

    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

And the class MyStar.

class MyStar {
  bool isEnable;
  int innerCirclePoints; //how many edges you need?
  double beamLength;
  double
      innerOuterRadiusRatio; // outter circle is x2 the inner // set star sharpness/chubbiness
  double angleOffsetToCenterStar;
  Offset center;
  Offset velocity;
  Color color;

  MyStar(
      {required this.isEnable,
      required this.innerCirclePoints,
      required this.beamLength,
      required this.innerOuterRadiusRatio,
      required this.angleOffsetToCenterStar,
      required this.center,
      required this.velocity,
      required this.color});
}

Solution 2

I would suggest using Lottie animations. If you make a quick search you can find the one that matches your needs:

https://lottiefiles.com/search?q=star&category=animations

If you found the right now click on it and press download -> lottie.json and then install this package in flutter:

https://pub.dev/packages/lottie

Then you simply add the downloaded json animation in your asset folder and reference it like this:

Lottie.asset(
  'assets/LottieLogo1.json',
  width: 200,
  height: 200,
  fit: BoxFit.fill,
  animate: true,
  repeat: true
),

With this you have an beautiful repeating animation. You can also use an controller to adapt everything even more.

Basically you can also make an animation in after effect and export it as an json animation with the bodymovin plugin

Share:
466
Joel
Author by

Joel

Updated on January 02, 2023

Comments

  • Joel
    Joel over 1 year

    I want to make a sparkling animation in flutter

    Animation:

    How to make this in flutter??

    • Joel
      Joel over 2 years
      Will add a bounty and give it to whoever answers it coz this is a rather difficult question
    • Yeasin Sheikh
      Yeasin Sheikh over 2 years
      yes based on highest vote, if you don't mark it as an answer, and This is a little complex work to do , there are two type of animations here as much as i get.
    • Joel
      Joel over 2 years
      Finally got a answer @YeasinSheikh
  • Joel
    Joel over 2 years
    Sorry but i'm looking for a pure flutter only animations, but the ppl who come accross this question can definitely try this out
  • DEFL
    DEFL over 2 years
    Ok sure. I will try to think about a solution for this. May I know the reason behind that?
  • Joel
    Joel over 2 years
    I actually wanted to implement this in flutter since lottie is pretty easy, I wanted to learn about animations like these, not many online tutorials exist ya know. All the ones are of "state animations" and none about "Element animation"
  • Joel
    Joel over 2 years
    WHOA!! didn't expect anyone to answer.... man u r amazing
  • mario francois
    mario francois over 2 years
    Glad to help you. If you want to understand more you can check this :uxdesign.cc/star-rating-make-svg-great-again-d4ce4731347e and obviously don't forget to dispose : @override void dispose() { animationController.dispose(); animation.removeListener(() { }); super.dispose(); }