Open In App

Flutter – Radial Hero Animation

Last Updated : 16 Oct, 2020
Improve
Improve
Like Article
Like
Save
Share
Report

A Radial transformation means turning a circular shape into a square shape. In Radial Hero animation the same is done with a Hero. In Flutter, a Hero Widget is used to create a hero animation. A hero in this context refers to a widget that moves in between screens. This is one of the most fundamental types of animation used in the application, especially if the app deals with media like images. Simply put, a hero animation is when a hero flies from one screen to another.

The following process occurs while creating a radial hero Animation:

  • The RadialExpansion Widget is used to wrap the hero.
  • The size of the hero changes as it flies through the routes and the hero adopts the size of the child.
  • Two overlapping clips form the RadialExpansion animation.
  • The PageRouteBuilder is used to build the destination of the hero.
  • All gestures are handled by the InkWell widget.

The following image shows the radial transformation of a hero at time = 0 to time =1.

Afterclipping

The RadialExpansion widget works in the following depicted way:

Now let’s build a simple app that has three heroes that make a radial transformation when clicked on them. The heroes will be stores in the following folder of the flutter app:

flutter_app/assets/images/.....

Make sure to add the dependencies in the pubspec.yaml file as shown below:

Now let’s build the main.dart file:

Dart




import 'dart:math' as math;
  
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
  
class Photo extends StatelessWidget {
  Photo({ Key key, this.photo, this.color, this.onTap }) : super(key: key);
  
  final String photo;
  final Color color;
  final VoidCallback onTap;
  
  Widget build(BuildContext context) {
    return Material(
      color: Theme.of(context).primaryColor.withOpacity(0.25),
      child: InkWell(
        onTap: onTap,
        child: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints size) {
            return Image.asset(
              photo,
              fit: BoxFit.contain,
            );
          },
        ),
      ),
    );
  }
}
  
class RadialExpansion extends StatelessWidget {
  RadialExpansion({
    Key key,
    this.maxRadius,
    this.child,
  }) : clipRectSize = 2.0 * (maxRadius / math.sqrt2),
        super(key: key);
  
  final double maxRadius;
  final clipRectSize;
  final Widget child;
  
  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: Center(
        child: SizedBox(
          width: clipRectSize,
          height: clipRectSize,
          child: ClipRect(
            child: child,
          ),
        ),
      ),
    );
  }
}
  
class RadialExpansionDemo extends StatelessWidget {
  static const double kMinRadius = 32.0;
  static const double kMaxRadius = 128.0;
  static const opacityCurve = const Interval(0.0, 0.75, curve: Curves.fastOutSlowIn);
  
  static RectTween _createRectTween(Rect begin, Rect end) {
    return MaterialRectCenterArcTween(begin: begin, end: end);
  }
  
  static Widget _buildPage(BuildContext context, String imageName, String description) {
    return Container(
      color: Theme.of(context).canvasColor,
      child: Center(
        child: Card(
          elevation: 8.0,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              SizedBox(
                width: kMaxRadius * 2.0,
                height: kMaxRadius * 2.0,
                child: Hero(
                  createRectTween: _createRectTween,
                  tag: imageName,
                  child: RadialExpansion(
                    maxRadius: kMaxRadius,
                    child: Photo(
                      photo: imageName,
                      onTap: () {
                        Navigator.of(context).pop();
                      },
                    ),
                  ),
                ),
              ),
              Text(
                description,
                style: TextStyle(fontWeight: FontWeight.bold),
                textScaleFactor: 3.0,
              ),
              const SizedBox(height: 20.0),
            ],
          ),
        ),
      ),
    );
  }
  
  Widget _buildHero(BuildContext context, String imageName, String description) {
    return Container(
      width: kMinRadius * 3.0,
      height: kMinRadius * 3.0,
      child: Hero(
        createRectTween: _createRectTween,
        tag: imageName,
        child: RadialExpansion(
          maxRadius: kMaxRadius,
          child: Photo(
            photo: imageName,
            onTap: () {
              Navigator.of(context).push(
                PageRouteBuilder<void>(
                  pageBuilder: (BuildContext context,
                                Animation<double> animation,
                                Animation<double> secondaryAnimation) {
                    return AnimatedBuilder(
                        animation: animation,
                        builder: (BuildContext context, Widget child) {
                          return Opacity(
                            opacity: opacityCurve.transform(animation.value),
                            child: _buildPage(context, imageName, description,),
  
                          );
                        }
                    );
                  },
                ),
              );
            },
          ),
        ),
      ),
    );
  }
  
  @override
  Widget build(BuildContext context) {
    // animation speed.
    timeDilation = 4.0;
  
    return Scaffold(
      appBar: AppBar(
        title: const Text('GeeksForGeeks'),
        backgroundColor: Colors.green,
      ),
      body: Container(
        padding: const EdgeInsets.all(33.0),
        alignment: FractionalOffset.bottomLeft,
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            _buildHero(context, 'assets/images/aquaman.png', 'Aquaman'),
            _buildHero(context, 'assets/images/superman.jpg', 'Superman'),
            _buildHero(context, 'assets/images/wonderwomen.jpg', 'Wonderwomen'),
          ],
        ),
      ),
    );
  }
}
  
void main() {
  runApp(MaterialApp(home: RadialExpansionDemo()));
}


Output:



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads