Created
November 16, 2019 22:37
-
-
Save onetdev/20d9da1aac63b8a08fdbaaece80f8629 to your computer and use it in GitHub Desktop.
Reveal route transition for Flutter
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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