In Android, there is a very interesting feature of the animation View, which is called CircularRevealAnimation
- literally "circular disclosure". Flutter, in turn, although it has rich capabilities for animating widgets, does not provide such animation from the box.
This article will show you how to implement this animation with Flutter and publish the library to pub.dev for easy access and distribution.
In Flutter, everything is a widget. And animation is no exception. Therefore, we will create the class CircularRevealAnimation
, which will extend the class StatelessWidget
.
Starting, stopping, and other animation management is done using the AnimationController
. To create an AnimationController
you need to inherit from StatefulWidget
and add the special class SingleTickerProviderStateMixin
to State
.
Our animation class CircularRevealAnimation
will not independently manage animation, but will receive Animation<double>
as a required constructor parameter, so there is no need to inherit from StatefulWidget
. This is done so that CircularRevealAnimation
can be easily combined with other animations using the same AnimationController
. For example, combine the disclosure animation with the transparency change animation.
Another important parameter of the CircularRevealAnimation
constructor is the child
, which is a child of our animation and will appear or disappear. In general, in Flutter, many widgets have a child
parameter. Such widgets allow you to change the behavior, drawing or location of the child widget. Or add an animation, as happens with CircularRevealAnimation
.
In addition, to set the animation, you will need parameters such as the center of the disclosure (or closure) of the animation, as well as the minimum and maximum radii of the disclosure. These parameters are optional and can be either null
or not specified when creating the animation. In this case, the default values will be used: the center of the disclosure will be in the center of the widget, the minimum radius will be wounded to zero, and the maximum radius will be equal to the distance from the center of the disclosure to the vertex of the widget that is furthest from the center of the disclosure.
The algorithm for calculating the maximum radius by default is as follows. First, calculate the distance horizontally and vertically from the center to the vertex furthest from the center of the disclosure, and then calculate the diagonal using the Pythagorean theorem.
static double calcMaxRadius(Size size, Offset center) { final w = max(center.dx, size.width - center.dx); final h = max(center.dy, size.height - center.dy); return sqrt(w * w + h * h); }
Now you need to implement trimming the widget within the circle during the drawing. With this, the ClipPath
class will help us, allowing you to trim a widget using an arbitrary pattern. As parameters, the clipper
passed to this widget (more about it later) and the child
is the child widget that needs to be trimmed.
The clipper
parameter of the ClipPath
widget determines how the child widget will be cropped. To create your own trim template, create a CircularRevealClipper
class that inherits the CustomClipper<Path>
class and override the Path getClip(Size size)
method. This method returns a Path
that bounds the clipping region. In our case, this area is a circle with a given center. To calculate the radius of a circle, you need to know the current value of the animation. This value is passed to CircularRevealClipper
as a fraction
parameter. The calculation of the radius of a circle is performed using linear interpolation between the minimum and maximum radii.
After that, let's move on to implementing the widget. Animation is convenient to use AnimatedBuilder
. The AnimatedBuilder
constructor accepts an Animation<double>
object and a builder
, which is used to build widgets based on the current animation value. In the builder
we create a ClipPath
and pass the current value of the animation ( fraction
) to CircularRevealClipper
.
class CircularRevealAnimation extends StatelessWidget { ... @override Widget build(BuildContext context) { return AnimatedBuilder( animation: animation, builder: (BuildContext context, Widget _) { return ClipPath( clipper: CircularRevealClipper( fraction: animation.value, center: center, minRadius: minRadius, maxRadius: maxRadius, ), child: this.child, ); }, ); } }
This completes the creation of CircularRevealAnimation
. It remains to use it. To do this, create a StatefulWidget
, AnimationController
and pass the AnimationController
to CircularRevealAnimation
.
import 'package:flutter/material.dart'; import 'package:circular_reveal_animation/circular_reveal_animation.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'CRA Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { AnimationController animationController; Animation<double> animation; @override void initState() { super.initState(); animationController = AnimationController( vsync: this, duration: Duration(milliseconds: 1000), ); animation = CurvedAnimation( parent: animationController, curve: Curves.easeIn, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("CRA Demo"), ), body: Padding( padding: const EdgeInsets.all(16.0), child: CircularRevealAnimation( minRadius: 12, maxRadius: 200, center: Offset(0, 300), child: Container(color: Colors.red), animation: animation, ), ), floatingActionButton: FloatingActionButton(onPressed: () { if (animationController.status == AnimationStatus.forward || animationController.status == AnimationStatus.completed) { animationController.reverse(); } else { animationController.forward(); } }), ); } }
Demo application on github.
To create a Dart or Flutter library, you need to add the pubspec.yaml
file to the same directory as the lib
directory with the Dart files. This file contains the library description, information about the authors and dependencies.
It is also a good practice to create a file that defines a public API. This file is located in the lib
folder and includes the name of the library and the list of files that need to be included in the public API. All other Dart-files are placed in the src
directory. This not only hides files that are not included in the public API, but also allows you to import a library using a single import
expression. Contents of this file :
library circular_reveal_animation; export 'package:circular_reveal_animation/src/circular_reveal_animation.dart';
In more detail about creation of libraries on Dart it is possible to read here .
Publishing the Dart library to pub.dev is very simple. All you need to do is run the flutter packages pub publish
command from the root directory of the library. The publication is carried out on behalf of the Google account, so the linking process will be given during the publication process, which must be opened in the browser and authorized by Google. Subsequently, it will be possible to publish updates only using the account on whose behalf the first version was posted.
Before publishing, it is recommended to check the library correctness using the flutter packages pub publish --dry-run
.
After executing the flutter packages pub publish
library will immediately be available at pub.dev. And, as written in the documentation, "Publishing is forever" - later you can only upload new versions. Old versions will also be available.
Although the publication of libraries looks simple, it can also have pitfalls. For example, when publishing the first version, I was shot a few points in the rating because the library description (in pubspec.yaml
) was too short.
More information about the publication of libraries can be found here .
Actually, the circular_reveal_animation
library on pub.dev and github.com .
PS: I used ```java {...} ```
to highlight the code on Dart. It would be nice to add a highlight of the code on Dart on habr.com.
Source: https://habr.com/ru/post/452072/
All Articles