这个路由提供页面从上下左右自由弹出的动画 ,其他不同形式的动画如法炮制。
import 'package:flutter/material.dart';
class PanPageRouteBuilder extends PageRoute {
final WidgetBuilder builder;
final AxisDirection popDirection;
PanPageRouteBuilder({
RouteSettings? settings,
required this.builder,
this.popDirection = AxisDirection.up,
this.transitionDuration = const Duration(milliseconds: 300),
this.reverseTransitionDuration = const Duration(milliseconds: 300),
this.opaque = true,
this.barrierDismissible = false,
this.barrierColor,
this.barrierLabel,
this.maintainState = true,
bool fullscreenDialog = false,
}) : assert(builder != null),
assert(opaque != null),
assert(barrierDismissible != null),
assert(maintainState != null),
assert(fullscreenDialog != null),
super(settings: settings, fullscreenDialog: fullscreenDialog);
@override
final Duration transitionDuration;
@override
final Duration reverseTransitionDuration;
@override
final bool opaque;
@override
final bool barrierDismissible;
@override
final Color? barrierColor;
@override
final String? barrierLabel;
@override
final bool maintainState;
@override
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) => builder(context);
@override
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
AxisDirection direction = isActive ? AxisDirection.up : AxisDirection.down; //上入下出
switch(popDirection) {
case AxisDirection.up :
direction = isActive ? AxisDirection.up : AxisDirection.down;
break;
case AxisDirection.down :
direction = isActive ? AxisDirection.down : AxisDirection.up;
break;
case AxisDirection.left :
direction = isActive ? AxisDirection.left : AxisDirection.right;
break;
case AxisDirection.right :
direction = isActive ? AxisDirection.right : AxisDirection.left;
break;
}
return PanTransition(
child: builder(context),
direction: direction,
position: animation,
);
}
}
class PanTransition extends AnimatedWidget {
PanTransition({
Key? key,
required Animation<double> position,
this.transformHitTests = true,
this.direction = AxisDirection.down,
required this.child,
}) : super(key: key, listenable: position) {
switch (direction) {
case AxisDirection.up:
_tween = Tween(begin: const Offset(0, 1), end: const Offset(0, 0));
break;
case AxisDirection.right:
_tween = Tween(begin: const Offset(-1, 0), end: const Offset(0, 0));
break;
case AxisDirection.down:
_tween = Tween(begin: const Offset(0, -1), end: const Offset(0, 0));
break;
case AxisDirection.left:
_tween = Tween(begin: const Offset(1, 0), end: const Offset(0, 0));
break;
}
}
final bool transformHitTests;
final Widget child;
final AxisDirection direction;
late final Tween<Offset> _tween;
@override
Widget build(BuildContext context) {
final position = listenable as Animation<double>;
Offset offset = _tween.evaluate(position);
if (position.status == AnimationStatus.reverse) {
switch (direction) {
case AxisDirection.up:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.right:
offset = Offset(-offset.dx, offset.dy);
break;
case AxisDirection.down:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.left:
offset = Offset(-offset.dx, offset.dy);
break;
}
}
return FractionalTranslation(
translation: offset,
transformHitTests: transformHitTests,
child: child,
);
}
}
使用
class RouteTestPage extends StatelessWidget {
const RouteTestPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: TextButton(onPressed: () {
Navigator.push(context, PanPageRouteBuilder(builder: (context) => const TargetPage(), popDirection: AxisDirection.up));
}, child: const Text("PUSH"),),),
backgroundColor: Colors.green,
);
}
}
class TargetPage extends StatelessWidget {
const TargetPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("POP", style: TextStyle(color: Colors.white),),
),),
backgroundColor: Colors.blueAccent,
);
}
}