Skip to content

Instantly share code, notes, and snippets.

@onetdev
Created November 16, 2019 22:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save onetdev/20d9da1aac63b8a08fdbaaece80f8629 to your computer and use it in GitHub Desktop.
Save onetdev/20d9da1aac63b8a08fdbaaece80f8629 to your computer and use it in GitHub Desktop.
Reveal route transition for Flutter
import 'dart:math' show sqrt, max;
import 'dart:ui' show lerpDouble;
import 'package:flutter/material.dart';
@immutable
class CircularRevealClipper extends CustomClipper<Path> {
final double fraction;
final Alignment centerAlignment;
final Offset centerOffset;
final double minRadius;
final double maxRadius;
CircularRevealClipper({
@required this.fraction,
this.centerAlignment,
this.centerOffset,
this.minRadius,
this.maxRadius,
});
@override
Path getClip(Size size) {
final Offset center = this.centerAlignment?.alongSize(size) ??
this.centerOffset ??
Offset(size.width / 2, size.height / 2);
final minRadius = this.minRadius ?? 0;
final maxRadius = this.maxRadius ?? calcMaxRadius(size, center);
return Path()
..addOval(
Rect.fromCircle(
center: center,
radius: lerpDouble(minRadius, maxRadius, fraction),
),
);
}
@override
bool shouldReclip(CustomClipper<Path> oldClipper) => true;
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);
}
}
import 'package:flutter/material.dart';
/// Since the clipper is not exported from circular_reveal_animation package
/// You will need to get that separately.
import 'circular_reveal_clipper.dart';
class RevealRoute extends PageRouteBuilder {
final Widget page;
final AlignmentGeometry centerAlignment;
final Offset centerOffset;
final double minRadius;
final double maxRadius;
/// Reveals the next item pushed to the navigation using circle shape.
///
/// You can provide [centerAlignment] for the reveal center or if you want a
/// more precise use only [centerOffset] and leave other blank.
///
/// The transition doesn't affect the entry screen so we will only touch
/// the target screen.
RevealRoute({
@required this.page,
this.minRadius = 0,
@required this.maxRadius,
this.centerAlignment,
this.centerOffset,
}) : assert(centerOffset != null || centerAlignment != null),
super(
/// We could override pageBuilder but it's a required parameter of
/// [PageRouteBuilder] and it won't build unless it's provided.
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
return page;
},
);
@override
Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) {
return ClipPath(
clipper: CircularRevealClipper(
fraction: animation.value,
centerAlignment: centerAlignment,
centerOffset: centerOffset,
minRadius: minRadius,
maxRadius: maxRadius,
),
child: child,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment