<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sidney Aguirre</title>
    <description>The latest articles on DEV Community by Sidney Aguirre (@sidney_pao).</description>
    <link>https://dev.to/sidney_pao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F134886%2F621634f5-29ed-460d-9005-24eff5631bbb.png</url>
      <title>DEV Community: Sidney Aguirre</title>
      <link>https://dev.to/sidney_pao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidney_pao"/>
    <language>en</language>
    <item>
      <title>Mouse Cursor Trail Effect with Flutter</title>
      <dc:creator>Sidney Aguirre</dc:creator>
      <pubDate>Fri, 08 Sep 2023 16:59:59 +0000</pubDate>
      <link>https://dev.to/sidney_pao/mouse-cursor-trail-effect-with-flutter-1fjc</link>
      <guid>https://dev.to/sidney_pao/mouse-cursor-trail-effect-with-flutter-1fjc</guid>
      <description>&lt;p&gt;Hello World!&lt;/p&gt;

&lt;p&gt;If you ask me for one of the UI effects that I enjoy the most I’d have to say the mouse trailing effect.&lt;br&gt;
It looks like a tail of the mouse cursor as it moves around the screen. Somehow like when you see a comet in the sky.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bafdgptj2254nc03z0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bafdgptj2254nc03z0x.png" alt="commet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today we will learn how to create this effect with Flutter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PS: If you were thinking that this works with animations, you are totally right. We’ll use some built-in Flutter animated widgets&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Cursor Trail Main Idea&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For our cursor trail, imagine that we have a paper sheet (our canvas) that the mouse cursor is our pencil, and that the trail is the trace we paint with our pencil .&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4q0c61il3bup4sg9u8ha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4q0c61il3bup4sg9u8ha.png" alt="pencil trail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this in mind, let’s begin:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On your Flutter project, add a new dart file called cursor_animated_trail.dart . Here we will add all the logic and clases to bring that trail to reality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;first, let’s create a class to handle the looks of each component in the trail. This will be called &lt;code&gt;AnimatedCursorTrailState&lt;/code&gt; , here we will set our widget looks state. &lt;code&gt;decoration&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt;.
Each element of the trail will be painted in a specific point in the canvas, that would be the &lt;code&gt;Offset&lt;/code&gt;. a pair of points x and y [:Offset(dx, dy)] understood as the position of an element in the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

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

class AnimatedCursorTrailState {
  const AnimatedCursorTrailState({
    this.decoration = defaultDecoration,
    this.offset = Offset.zero,
    this.size = defaultSize,
  });

  static const Size defaultSize = Size(20, 20);
  static const BoxDecoration defaultDecoration = BoxDecoration(
    color: Colors.pink,
  );

  final BoxDecoration decoration;
  final Offset offset;
  final Size size;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Until here, I haven’t said what the widget is specificly, just how it might look like, not what it looks like yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, we’ll add a &lt;code&gt;AnimatedCursorTrailProvider&lt;/code&gt; class on which we will handle the behavior of our cursor in order to paint the trail. This class extends from &lt;code&gt;ChangeNotifier&lt;/code&gt; as we need other to be notified of that behavior so they can react accordingly.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedCursorTrailProvider extends ChangeNotifier {
  AnimatedCursorTrailProvider();

  AnimatedCursorTrailState state = AnimatedCursorTrailState();

  void updateCursorPosition(Offset position) {
    state = AnimatedCursorTrailState(offset: position);
    notifyListeners();
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;In this class, first, we create an instance of &lt;code&gt;AnimatedCursorTrailState&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;then we have one &lt;code&gt;methodupdateCursorPosition()&lt;/code&gt; that will update the position of our trail.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now let’s create the trail itself.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedCursorTrail extends StatelessWidget {
  const AnimatedCursorTrail({
    super.key,
    this.child,
  });

  final Widget? child;

  void _onCursorUpdate(BuildContext context, PointerEvent event) =&amp;gt;
      context.read&amp;lt;AnimatedCursorTrailProvider&amp;gt;().updateCursorPosition(
            event.position,
          );

  List&amp;lt;Widget&amp;gt; _trail(AnimatedCursorTrailProvider provider) {
    final result = &amp;lt;Widget&amp;gt;[];

    for (var index = 0; index &amp;lt; provider.listTrail.length; index++) {
      if (index % 10 == 1) {
        result.add(provider.listTrail.elementAt(index));
      }
    }

    return result;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) =&amp;gt; c(),
      child: Consumer&amp;lt;AnimatedCursorTrailProvider&amp;gt;(
        builder: (context, provider, child) {
          final state = provider.state;

          return Stack(
            children: [
              if (child != null) child,
              Positioned.fill(
                child: MouseRegion(
                  onHover: (event) {
                    _onCursorUpdate(context, event);
                    provider.listTrail.add(
                      AnimatedTrail(offset: event.position),
                    );
                  },
                  opaque: false,
                ),
              ),
              AnimatedPositioned(
                left: state.offset.dx - state.size.width / 2,
                top: state.offset.dy - state.size.height / 2,
                width: state.size.width,
                height: state.size.height,
                duration: Duration(milliseconds: 50),
                child: IgnorePointer(
                  child: Icon(
                      Icons.star,
                      color: state.decoration.color,
                      size: state.size.height,
                  ),
                ),
              ),
            ],
          );
        },
        child: child,
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, we have the &lt;code&gt;AnimatedCursorTrail&lt;/code&gt; stateless widget that receives a child as a parameter, this is because this widget will work as a &lt;strong&gt;wrapper of our content&lt;/strong&gt; and will paint the trail where we want it.&lt;/p&gt;

&lt;p&gt;in the &lt;code&gt;build&lt;/code&gt; method, we have a &lt;code&gt;ChangeNotifierProvider&lt;/code&gt; widget whose child is a &lt;code&gt;Consumer&lt;/code&gt; of &lt;code&gt;AnimatedCursorTrailProvider&lt;/code&gt; . This means that &lt;code&gt;ChangeNotifierProvider&lt;/code&gt; will be able to listen when the state changes in &lt;code&gt;AnimatedCursorTrailProvider&lt;/code&gt;, exposing this changes to its descendants.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Consumer&lt;/code&gt; child will get the &lt;code&gt;Provider&lt;/code&gt; from its parent and will pass it to the builder so it can take the changes and animate the trail.&lt;/p&gt;

&lt;p&gt;in the builder we return a &lt;code&gt;Stack&lt;/code&gt; widget whose deepest element will be &lt;code&gt;child&lt;/code&gt; , then we have a &lt;code&gt;MouseRegion&lt;/code&gt; widget that will capture the mouse movement event and a our trail.&lt;/p&gt;

&lt;p&gt;The actual trail component is this piece here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa78x1ysn663qs91a93u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwa78x1ysn663qs91a93u.png" alt="actual trail component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you noticed, this trail is an &lt;code&gt;AnimatedPositioned&lt;/code&gt; that takes the offset of our pointer (captured in the &lt;code&gt;Consumer&lt;/code&gt; builder method) and moves our trail widget towards the pointer, that’s why its a trail, ain’t it?&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;AnimatedPositioned&lt;/code&gt; is wrapping an &lt;code&gt;IgnorePointer&lt;/code&gt; widget so our clicking events are not interfered. There is some star icon there, probably how the trail will look like.&lt;/p&gt;

&lt;p&gt;In order to see the result on the screen of what we just did, let’s use the wrapper we mention before, Just wrap your widget with &lt;code&gt;AnimatedCursorTrail&lt;/code&gt;, and Voilá!&lt;/p&gt;

&lt;p&gt;In my case, the project I have is this &lt;a href="https://codelabs.developers.google.com/codelabs/flutter-codelab-first?hl=en&amp;amp;authuser=2#6" rel="noopener noreferrer"&gt;Namer app&lt;/a&gt; from one of &lt;a href="https://codelabs.developers.google.com/?product=flutter" rel="noopener noreferrer"&gt;Google Flutter Codelabs&lt;/a&gt;. I wrapped the &lt;code&gt;GeneratorPage&lt;/code&gt; with &lt;code&gt;AnimatedCursorTrail&lt;/code&gt; , passed the page content as the child and this is the result:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Widget build(BuildContext context) {

   return AnimatedCursorTrail(
      child: Scaffold(
           //Page content...
       ),
    );
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxhmvnhk8ymufchizts6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxhmvnhk8ymufchizts6.gif" alt="star pointer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oops! this doesn’t look like a trail at all! It looks like our star icon feels lonely so it just follows the pointer, and that’s it 😅&lt;/p&gt;

&lt;p&gt;Well, for the trail we can think of it as a repetition of a widget. Each widget will be painted in a specific point in the canvas, right?&lt;br&gt;
but if “Trail ::= a repetition of a widget”, how can we multiply a widget?&lt;/p&gt;

&lt;p&gt;Simple! We add the same widget multiple times to some List and paint every element on the list. So, With this in mind let’s modify a little bit our implementation:&lt;/p&gt;

&lt;p&gt;First, Let’s move our tail widget out to a new class:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedTrail extends StatelessWidget {
  const AnimatedTrail({
    super.key,
    required this.offset,
  });

  final Offset offset;

  @override
  Widget build(BuildContext context) {
    return Positioned(
      left: widget.offset.dx,
      top: widget.offset.dy,
      child: IgnorePointer(
        child: Icon(
          Icons.star,
          color: AnimatedCursorTrailState.defaultDecoration.color,
          size: AnimatedCursorTrailState.defaultSize.height,
        ),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you see, We no longer have an &lt;code&gt;AnimatedPositioned&lt;/code&gt; widget, since this trail individual widget will be at a specific point in the screen, right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;List strategy to build Trail&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s say that inside our AnimatedCursorTrail widget, we create an empty list in which we will add the trail components as the cursor moves, like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedCursorTrail extends StatelessWidget {
  ...

  final trail = &amp;lt;Widget&amp;gt;[]; // Here 

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      ...

          return Stack(
            children: [
              if (child != null) child,
              Positioned.fill(
                child: MouseRegion(
                  onHover: (event) {
                    trail.add(                 // From here
                      AnimatedTrail(           // Add element to trail
                        offset: event.position,   
                      ),
                    );                         // To here
                    _onCursorUpdate(context, event);
                  },
                  opaque: false,
                ),
              ),
              for (var star in trail) star, // Trail here
            ],
          );
        },
        child: widget.child,
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the &lt;code&gt;onHover(event)&lt;/code&gt; method, as the cursor moves we add a star icon element to create the trail, and paint the actual trail using a for loop.&lt;br&gt;
Notice that we can see the changes without having a &lt;code&gt;StatefulWidget&lt;/code&gt;?&lt;br&gt;
That is because the method &lt;code&gt;_onCursorUpdate()&lt;/code&gt; results in a notification to the &lt;code&gt;AnimatedCursorTrailProvider&lt;/code&gt; subscribers.&lt;/p&gt;

&lt;p&gt;If you reload and check, you’ll see that now we can draw a cursor trail 🥳 (although each point stays in the screen 🤨) something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqqmpnkxx2heb59z28es.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiqqmpnkxx2heb59z28es.png" alt="doodle board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Humk, we don’t need a doodle board, 🤔 that's a different project!&lt;br&gt;
(although, if that’s what you wanted, you got it already and can stop here 🤗)&lt;/p&gt;

&lt;p&gt;For those looking for a Trail, we want each point to desapear nicely after some time. Ok! let's wrap our star icon with an &lt;code&gt;AnimatedOpacity&lt;/code&gt; widget, since this will make that point fade out and avoids that it lingers in the screen. Now,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Since this animated widget requires some updates in its state, we convert AnimatedTrail to a StatefulWidget.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create a double value called opacityLevel initialized in 1.0, so the icon is fully visible once it’s created&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;create a method changeOpacity() to assing 0.0 to opacityLevel so it becomes ‘invisible’&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;inside the build method, call WidgetsBinding.instance.addPostFrameCallback in order to start animation right after the element is built.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll get something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedTrail extends StatefulWidget {
  ...

  double opacityLevel = 1.0;

  void changeOpacity() {
    if (mounted) setState(() =&amp;gt; opacityLevel = 0.0);
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      changeOpacity();
    });

    return Positioned(
      left: widget.offset.dx - 250,
      top: widget.offset.dy,
      child: IgnorePointer(
        child: AnimatedOpacity(
          duration: Duration(seconds: 3),
          curve: Curves.ease,
          opacity: opacityLevel,
          child: Icon(
            Icons.favorite,
            color: AnimatedCursorTrailState.defaultDecoration.color,
            size: AnimatedCursorTrailState.defaultSize.height,
          ),
        ),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Reload and Voilá:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1womlyahmfy6yn5pide4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1womlyahmfy6yn5pide4.gif" alt="trail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To organize our code better, let’s move the list (trail) to a place where it seems to belong and make things easier for us:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

class AnimatedCursorTrailProvider extends ChangeNotifier {
  AnimatedCursorTrailProvider();

  AnimatedCursorTrailState state = AnimatedCursorTrailState();
  final trail = &amp;lt;Widget&amp;gt;[];

  void updateCursorPosition(Offset position) {
    state = AnimatedCursorTrailState(offset: position);
    notifyListeners();
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you want, you can have some condition to paint the trail so it doen’t paint a star in every single point but on most of them. This will look smother too!&lt;/p&gt;

&lt;p&gt;Here, I just add the items to the trail if index mod(30) is 0.&lt;/p&gt;

&lt;p&gt;💫&lt;/p&gt;

&lt;p&gt;Check the full implementation here:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';
import 'package:my_website/my_website.dart';
import 'package:provider/provider.dart';

class AnimatedCursorTrailState {
  const AnimatedCursorTrailState({
    this.decoration = defaultDecoration,
    this.offset = Offset.zero,
    this.size = defaultSize,
  });

  static const Size defaultSize = Size(20, 20);
  static const BoxDecoration defaultDecoration = BoxDecoration(
    borderRadius: BorderRadius.all(
      Radius.circular(90),
    ),
    color: Colors.purple,
  );

  final BoxDecoration decoration;
  final Offset offset;
  final Size size;
}

class AnimatedCursorTrailProvider extends ChangeNotifier {
  AnimatedCursorTrailProvider();

  AnimatedCursorTrailState state = AnimatedCursorTrailState();
  final listTrail = &amp;lt;Widget&amp;gt;[];

  void changeCursor(GlobalKey key, {BoxDecoration? decoration}) {
    final renderBox = key.currentContext?.findRenderObject() as RenderBox?;

    if (renderBox == null) return;

    state = AnimatedCursorTrailState(
      decoration: decoration ?? AnimatedCursorTrailState.defaultDecoration,
      offset: renderBox.localToGlobal(Offset.zero).translate(
            renderBox.size.width / 2,
            renderBox.size.height / 2,
          ),
      size: renderBox.size,
    );

    notifyListeners();
  }

  void resetCursor() {
    state = AnimatedCursorTrailState();
    notifyListeners();
  }

  void updateCursorPosition(Offset position) {
    state = AnimatedCursorTrailState(offset: position);
    notifyListeners();
  }
}

class AnimatedCursorTrail extends StatelessWidget {
  const AnimatedCursorTrail({
    super.key,
    this.child,
  });

  final Widget? child;

  void _onCursorUpdate(BuildContext context, PointerEvent event) =&amp;gt;
      context.read&amp;lt;AnimatedCursorTrailProvider&amp;gt;().updateCursorPosition(
            event.position,
          );

  List&amp;lt;Widget&amp;gt; _trail(AnimatedCursorTrailProvider provider) {
    final result = &amp;lt;Widget&amp;gt;[];

    for (var index = 0; index &amp;lt; provider.listTrail.length; index++) {
      if (index % 30 == 0) {
        result.add(provider.listTrail.elementAt(index));
      }
    }

    return result;
  }

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) =&amp;gt; AnimatedCursorTrailProvider(),
      child: Consumer&amp;lt;AnimatedCursorTrailProvider&amp;gt;(
        builder: (context, provider, child) {
          return Stack(
            children: [
              if (child != null) child,
              Positioned.fill(
                child: MouseRegion(
                  onHover: (event) {
                    _onCursorUpdate(context, event);
                    provider.listTrail.add(
                      AnimatedTrail(offset: event.position),
                    );
                  },
                  opaque: false,
                ),
              ),
              ..._trail(provider),
            ],
          );
        },
        child: child,
      ),
    );
  }
}

class AnimatedCursorMouseRegion extends StatefulWidget {
  const AnimatedCursorMouseRegion({
    super.key,
    this.child,
  });

  final Widget? child;

  @override
  State&amp;lt;AnimatedCursorMouseRegion&amp;gt; createState() =&amp;gt;
      _AnimatedCursorMouseRegionState();
}

class _AnimatedCursorMouseRegionState extends State&amp;lt;AnimatedCursorMouseRegion&amp;gt; {
  late final AnimatedCursorTrailProvider _cubit;
  final GlobalKey _key = GlobalKey();

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

    _cubit = context.read&amp;lt;AnimatedCursorTrailProvider&amp;gt;();
  }

  @override
  void dispose() {
    _cubit.resetCursor();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      key: _key,
      opaque: false,
      onExit: (_) =&amp;gt; _cubit.resetCursor(),
      child: widget.child,
    );
  }
}

class AnimatedTrail extends StatefulWidget {
  const AnimatedTrail({
    super.key,
    required this.offset,
  });

  final Offset offset;

  @override
  State&amp;lt;AnimatedTrail&amp;gt; createState() =&amp;gt; _AnimatedTrailState();
}

class _AnimatedTrailState extends State&amp;lt;AnimatedTrail&amp;gt; {
  double opacityLevel = 1.0;

  void changeOpacity() {
    if (mounted) setState(() =&amp;gt; opacityLevel = 0.0);
  }

  @override
  Widget build(BuildContext context) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      changeOpacity();
    });

    return Positioned(
      left: widget.offset.dx - navBarWidth,
      top: widget.offset.dy,
      child: IgnorePointer(
        child: AnimatedOpacity(
          duration: Duration(seconds: 3),
          curve: Curves.ease,
          opacity: opacityLevel,
          child: Icon(
            Icons.favorite,
            color: AnimatedCursorTrailState.defaultDecoration.color,
            size: AnimatedCursorTrailState.defaultSize.height,
          ),
        ),
      ),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The end! I hope you enjoyed this 🥹&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;References&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cursor Mouse Follow Animation in Flutter -&amp;gt; &lt;a href="https://www.youtube.com/watch?v=CCrmRG-dIfY" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=CCrmRG-dIfY&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Flutter Animations</title>
      <dc:creator>Sidney Aguirre</dc:creator>
      <pubDate>Wed, 05 Jul 2023 17:23:09 +0000</pubDate>
      <link>https://dev.to/sidney_pao/flutter-animations-gpo</link>
      <guid>https://dev.to/sidney_pao/flutter-animations-gpo</guid>
      <description>&lt;p&gt;&lt;strong&gt;Movement represents life&lt;/strong&gt; or makes us thing about life. If something is static, we probably wont interact with it as easily as we do with things that have some motion. &lt;/p&gt;

&lt;p&gt;Humans interact with digital products in a daily basis, so inlcuding Animations in our applications is important since they &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make interactions more intuitive and natural&lt;/li&gt;
&lt;li&gt;provide user feedback and a nice user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is an Animation?
&lt;/h2&gt;

&lt;p&gt;We all relate animation to movement, but in programming what does it really mean?&lt;/p&gt;

&lt;p&gt;Well... an animation is defined as point &lt;strong&gt;values that change over time&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GExg2Qei--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdycrmncv9s81fui4occ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GExg2Qei--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sdycrmncv9s81fui4occ.gif" alt="curve" width="464" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Animations
&lt;/h2&gt;

&lt;p&gt;There are 2 types of animations&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Drawing-based animations&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;2. Code-based animations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Drawing-based animations&lt;/strong&gt; can be achieved by using some libraries and programs like lottie, flare, etc&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NYgrFID1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7so7bezluyda00zcry2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NYgrFID1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f7so7bezluyda00zcry2.gif" alt="Cat and Butterfly" width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we will focus on &lt;strong&gt;code-based animations&lt;/strong&gt; in Flutter &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JUVfqKUW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/40znlcmfz5dldyzxarro.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JUVfqKUW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/40znlcmfz5dldyzxarro.gif" alt="Loader" width="250" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Flutter animations
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AFd2wWPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvfvu3i78ocxdd5q7lpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AFd2wWPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wvfvu3i78ocxdd5q7lpy.png" alt="Flutter animation types" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Implicit Animations
&lt;/h3&gt;

&lt;p&gt;For Implicit Animations you are in charge only for &lt;strong&gt;setting the new value&lt;/strong&gt; of the property you are animating according to some validation. They are called implicit because the framework does all the math for transitioning between starting and end points for the given curve. &lt;br&gt;
This property can be size, opacity, aligment, color, etc. &lt;/p&gt;

&lt;p&gt;In this example, we have an animated container whose child is an image, this container shrinks when clicked:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N9jhiUSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i793w6012oig0gioh6u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N9jhiUSL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2i793w6012oig0gioh6u.png" alt="code container shrinking" width="800" height="681"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uTptFslg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iof9yblwflhswg7g9fiz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uTptFslg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iof9yblwflhswg7g9fiz.gif" alt="Robot in container" width="330" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to achieve an implicit animation, Flutter provides an extensive catalogue  of built-in animated widgets, where you only change the value of the animated property, as mentioned above. &lt;/p&gt;

&lt;p&gt;These animated widgets are named as ["Animated"${widgetName}]&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aKI0tTWH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kubzdeqh58ga00j39ekc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aKI0tTWH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kubzdeqh58ga00j39ekc.png" alt="AnimatedSomething" width="511" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some built-in animated widgets are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AnimatedAlign&lt;/li&gt;
&lt;li&gt;AnimatedBuilder&lt;/li&gt;
&lt;li&gt;AnimatedContainer&lt;/li&gt;
&lt;li&gt;AnimatedOpacity&lt;/li&gt;
&lt;li&gt;AnimatedPadding&lt;/li&gt;
&lt;li&gt;AnimatedPositioned&lt;/li&gt;
&lt;li&gt;AnimatedRotation&lt;/li&gt;
&lt;li&gt;AnimatedScale&lt;/li&gt;
&lt;li&gt;AnimatedSize&lt;/li&gt;
&lt;li&gt;AnimatedSlide&lt;/li&gt;
&lt;li&gt;AnimatedSwitcher&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;check the full list of built-in animated widgets and documentation &lt;a href="https://api.flutter.dev/flutter/widgets/AnimatedWidget-class.html"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Implicit animations
&lt;/h4&gt;

&lt;p&gt;You can also build Custom Implicit animations by using &lt;strong&gt;Tween Animation&lt;/strong&gt; (Tween stands for &lt;strong&gt;in-betweening&lt;/strong&gt;).&lt;br&gt;
To make it custom, &lt;strong&gt;define the initial points and the end points&lt;/strong&gt; of the animation, as well as the curve and duration.&lt;/p&gt;

&lt;p&gt;these beginning and end "points" can be properties too, as a colors, for example. &lt;/p&gt;

&lt;p&gt;See that the colors in between are never explicitly defined, but calculated by the framework:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QBby7KDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9n35naxfthavg0og69lc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QBby7KDq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9n35naxfthavg0og69lc.png" alt="code button" width="800" height="654"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ctS0dEbL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pglu1vaw2yj2kaqvu8qj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ctS0dEbL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pglu1vaw2yj2kaqvu8qj.gif" alt="Button changing color" width="330" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Staggered animations
&lt;/h4&gt;

&lt;p&gt;It is possible to chain sequences of tweens into the animation, to achieve more changes in the animation. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1zvSeSpu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ae1vcp1rdwyes91rpfdx.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1zvSeSpu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ae1vcp1rdwyes91rpfdx.gif" alt="tween sequence" width="600" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use Implicit animations
&lt;/h4&gt;

&lt;p&gt;Use implicit animations when you need it to start and finish after some interaction event. These animations does not repeat indefinitely.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Explicit Animations
&lt;/h3&gt;

&lt;p&gt;In the Explicit Animations you are in charge of declaring an &lt;strong&gt;Animation Controller&lt;/strong&gt; that will allow you to control the animation.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Control the animation how?&lt;/em&gt;&lt;br&gt;
You can make it go forward, reverse it, repeat it, or stop it. This is why they are called explicit animations, because you define explicitly how and when the animation should behabe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--llnuFOgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apc44pssccpvueyksu2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--llnuFOgt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/apc44pssccpvueyksu2d.png" alt="code cd" width="785" height="702"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nrFJuttZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ra97vlcfqp7v4c0aghy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nrFJuttZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ra97vlcfqp7v4c0aghy.gif" alt="cd rotating" width="300" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to achieve an explicit animation, Flutter provides an extensive catalogue of built-in transitioning widgets&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6Gb3yzj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flmqlo7ns99tndajc93y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6Gb3yzj---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flmqlo7ns99tndajc93y.png" alt="built-in transitioning widgets" width="443" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some built-in transitioning widgets are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AlignTransition&lt;/li&gt;
&lt;li&gt;FadeTransition&lt;/li&gt;
&lt;li&gt;PositionedTransition&lt;/li&gt;
&lt;li&gt;ScaleTransition&lt;/li&gt;
&lt;li&gt;SizeTransition&lt;/li&gt;
&lt;li&gt;RotationTransition&lt;/li&gt;
&lt;li&gt;SlideTransition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;check these widgets documentation &lt;a href="https://docs.flutter.dev/ui/widgets/animation"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Custom Explicit animations
&lt;/h4&gt;

&lt;p&gt;You can also build Custom Explicit animations by using &lt;strong&gt;Animated Builder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sOIiqm_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci4ievfygxkgxx6swfwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sOIiqm_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ci4ievfygxkgxx6swfwo.png" alt="Code star" width="800" height="668"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wbABuDpM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5d4wc6x9uk31i149em17.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wbABuDpM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5d4wc6x9uk31i149em17.gif" alt="Star shining" width="230" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  When to use Explicit animations
&lt;/h4&gt;

&lt;p&gt;Use explicit animations when you need it to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;do indefinite repetition&lt;/li&gt;
&lt;li&gt;discontinuity (as you can control if it starts or stops)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Curves
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--z3c8eo1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vq37ntpu1x2tnthr46wb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--z3c8eo1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vq37ntpu1x2tnthr46wb.gif" alt="curves" width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check available animation curves &lt;a href="https://api.flutter.dev/flutter/animation/Curves-class.html"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and check &lt;a href="https://api.flutter.dev/flutter/animation/animation-library.html"&gt;this documentation&lt;/a&gt; for more!&lt;/p&gt;

&lt;p&gt;Thanks for reading this article, hopefully you found it helpful 🦋&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>animation</category>
      <category>ux</category>
      <category>dart</category>
    </item>
  </channel>
</rss>
