We implement CircularRevealAnimation on Flutter and simultaneously publish the library on pub.dev

  • Tutorial

In Android, there is a very interesting feature of the View animation, which is called CircularRevealAnimation- literally, "circular disclosure." Flutter, in turn, although it has rich capabilities for animating widgets, does not provide such animation out of the box.



This article will show how to implement such animation using Flutter and publish the library on pub.dev for easy access and distribution.


Animation implementation


In Flutter, everything is a widget. And animation is no exception. Therefore, we will create a class CircularRevealAnimationthat will extend the class StatelessWidget.


Starting, stopping and other animation control is carried out using AnimationController. To create, AnimationControlleryou need to inherit from StatefulWidgetand add to a Statespecial class SingleTickerProviderStateMixin.


Our animation class CircularRevealAnimationwill not be independently engaged in managing the animation, but will receive it as a required parameter of the constructor, so there is no need to inherit from . This is done so that you can easily combine with other animations that use the same . For example, combine disclosure animation with transparency change animation.AnimationStatefulWidgetCircularRevealAnimationAnimationController


Another important parameter of the constructor CircularRevealAnimationis that childwhich is a child widget of our animation, and which will appear or disappear. In general, in Flutter, a lot of widgets have a parameter child. Such widgets allow you to change the behavior, rendering, or layout of a child widget. Or add an animation like this with CircularRevealAnimation.


In addition, to specify the animation, parameters such as the center of the opening (or closing) of the animation, as well as the minimum and maximum radius of the opening, will be required. These parameters are optional and can be specified as nullor not at all when creating the animation. In this case, the default values ​​will be used: the disclosure center 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 disclosure center to the vertex of the widget that is farthest from the disclosure center.


The default maximum radius calculation algorithm is as follows. First, the horizontal and vertical distances from the center to the vertex farthest from the disclosure center are calculated, and then the diagonal is calculated by 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 the cropping of the widget within the circle during rendering. This will help us with a class ClipPaththat allows you to crop the widget according to an arbitrary template. As parameters, this widget is passed clipper(about it a bit later) and childis the child widget that needs to be cropped.


The clipperwidget parameter ClipPathdetermines how the child widget will be cropped. To create your own trim template, create a class CircularRevealClipperthat inherits from the class and redefine the method . This method returns bounding the cropping area. In our case, this region 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 in as a parameter . The radius of the circle is calculated using linear interpolation between the minimum and maximum radii.CustomClipperPath getClip(Size size)PathCircularRevealClipperfraction


After that, let's move on to the implementation of the widget. It’s convenient to use to create an animation AnimatedBuilder. The constructor AnimatedBuildertakes an object and used to build widgets based on the current animation value. In we create and pass the current animation value ( ) to .AnimationbuilderbuilderClipPathfractionCircularRevealClipper


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 CircularRevealAnimationcompletes the creation . It remains to use it. To do this, create StatefulWidget, AnimationControllerand transfer AnimationControllerto CircularRevealAnimation.


Usage example
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
    with SingleTickerProviderStateMixin {
  AnimationController animationController;
  Animation 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();
        }
      }),
    );
  }
}

Github demo app .


Library creation


To create a Dart or Flutter library, you need to add the file pubspec.yamlto the same directory as the directory libwith the Dart files. This file contains a description of the library, information about authors and dependencies.


It is also good practice to create a file defining a public API. This file is located in the folder liband includes the name of the library and a list of files that need to be included in the public API. All other Dart files are placed in the directory src. This not only hides files that are not included in the public API, but also allows you to import the library using a single importexpression. The contents of this file :


library circular_reveal_animation;
export 'package:circular_reveal_animation/src/circular_reveal_animation.dart';

You can read more about creating libraries on Dart here .


Publish to pub.dev


Publishing a Dart library to pub.dev is very simple. All you need to do is run the command flutter packages pub publishfrom the root directory of the library. Publishing is carried out on behalf of a Google account, so during the publication process a link will be given that must be opened in a browser and logged in to Google. Subsequently, updates can be published only using the account on whose behalf the first version was posted.


Before publishing, it is recommended that you verify the library is correct using the command flutter packages pub publish --dry-run.


After execution, the flutter packages pub publishlibrary will immediately become available on pub.dev. And, as the documentation says, "Publishing is forever" - later you can only upload new versions. Older versions will also be available.


Although library publishing looks simple, it can also have pitfalls. For example, when publishing the first version, they took off a few points in the rating because the description of the library (c pubspec.yaml) was too short.


You can read more about publishing libraries here .


Actually, the library circular_reveal_animationon pub.dev and github.com .


PS: I used ```java {...} ```to highlight the code on Dart. It would be nice to add Dart code highlighting on habr.com.


Also popular now: