当我使用
Navigator.pushNamed(context, "/someRoute");
时,有一个最小的动画从屏幕底部在新路线中滑动(在Android上,在iOS上可能看起来不同)。
如何向此过渡添加自定义动画?
我找到了这篇文章,其中有一些非常简洁的未命名路线示例代码。他们实现了自己的类,该类继承自
PageRouteBuilder
并且可以像这样使用:Navigator.push(context, SlideRightRoute(page: Screen2()))
。但 PageRouteBuilder
不是 Widget,不能在 MaterialApp
中注册为路线。所以我不知道如何将其应用于命名路由。
您需要在
onGenerateRoute
小部件中使用
MaterialApp
。
onGenerateRoute: (settings) {
if (settings.name == "/someRoute") {
return PageRouteBuilder(
settings: settings, // Pass this to make popUntil(), pushNamedAndRemoveUntil(), works
pageBuilder: (_, __, ___) => SomePage(),
transitionsBuilder: (_, a, __, c) => FadeTransition(opacity: a, child: c)
);
}
// Unknown route
return MaterialPageRoute(builder: (_) => UnknownPage());
},
我找到了一个简单的解决方案(灵感来自此代码)
首先需要为MaterialApp设置一个静态的GlobalKey并导出
static GlobalKey mtAppKey = GlobalKey();
Widget build(BuildContext context) {
return MaterialApp(
key: MyApp.mtAppKey,
...
此外,您需要一个自定义的 PageRouteBuilder 来处理它
零安全禁用
class CustomNamedPageTransition extends PageRouteBuilder {
CustomNamedPageTransition(
GlobalKey materialAppKey,
String routeName, {
Object arguments,
}) : super(
settings: RouteSettings(
arguments: arguments,
name: routeName,
),
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
assert(materialAppKey.currentWidget != null);
assert(materialAppKey.currentWidget is MaterialApp);
var mtapp = materialAppKey.currentWidget as MaterialApp;
var routes = mtapp.routes;
assert(routes.containsKey(routeName));
return routes[routeName](context);
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity: animation,
child: child,
),
transitionDuration: Duration(seconds: 1),
);
}
启用零安全
class CustomNamedPageTransition extends PageRouteBuilder {
CustomNamedPageTransition(
GlobalKey materialAppKey,
String routeName, {
Object? arguments,
}) : super(
settings: RouteSettings(
arguments: arguments,
name: routeName,
),
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) {
assert(materialAppKey.currentWidget != null);
assert(materialAppKey.currentWidget is MaterialApp);
var mtapp = materialAppKey.currentWidget as MaterialApp;
var routes = mtapp.routes;
assert(routes!.containsKey(routeName));
return routes![routeName]!(context);
},
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity: animation,
child: child,
),
transitionDuration: Duration(seconds: 1),
);
}
然后,您可以使用
打开您的命名路线Navigator.push(
context,
CustomNamedPageTransition(
MyApp.mtAppKey,
MyRoute.routeName,
),
);
或
Navigator.pushReplacement(
context,
CustomNamedPageTransition(
MyApp.mtAppKey,
MyRoute.routeName,
),
);
无需
onGenerateRoute
即可使用动画路线!
如果您使用
MaterialApp
的 routes
映射来定义命名路由,以下是如何定义命名路由(其名称不会是 null
)。
只需通过扩展
PageRouteBuilder
来创建你的路线:
import 'package:flutter/material.dart';
class FadeInRoute extends PageRouteBuilder {
final Widget page;
FadeInRoute({this.page, String routeName})
: super(
settings: RouteSettings(name: routeName), // set name here
pageBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
) =>
page,
transitionsBuilder: (
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
) =>
FadeTransition(
opacity: animation,
child: child,
),
transitionDuration: Duration(milliseconds: 500),
);
}
然后当您导航时,只需执行以下操作:
Navigator.push( // or pushReplacement, if you need that
context,
FadeInRoute(
routeName: RouteNames.home,
page: MyHomeScreen(),
),
);
创建动画过渡并使用
popUntil
和命名路线 不需要 需要使用 onGenerateRoute
。您只需在创建 routeName
时再次指定 PageRouteBuilder
即可。
修改 Flutter docs 中的示例,维护对路由的命名引用可以通过在
settings
中添加 PageRouteBuilder
参数来实现:
Route _createRoute() {
return PageRouteBuilder(
settings: RouteSettings(name: '/new-screen'),
pageBuilder: (context, animation, _) => const NewScreen(),
transitionsBuilder: (context, animation, _, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
}
简单地使用
Navigator.of(context).push(_createRoute())
; 来调用
屏幕
NewScreen
可以在MaterialApp
路线下正常注册:
MaterialApp(
...
...
routes: {
...
'/new-screen': (context) => NewScreen(),
}
)
您可以修改上面的代码以使其更加动态。 也就是说,使用
onGeneratedRoute
是更永久的解决方案
使用以下代码在 flutter 中为命名路线设置动画
首先构建过渡动画:
class SizeTransitionAnimation extends PageRouteBuilder {
final Widget page;
SizeTransitionAnimation({required this.page})
: super(
pageBuilder: (context, animation, anotherAnimation) => page,
transitionDuration: Duration(milliseconds: 1000),
reverseTransitionDuration: Duration(milliseconds: 200),
transitionsBuilder: (context, animation, anotherAnimation, child) {
animation = CurvedAnimation(
parent: animation,
curve: Curves.fastLinearToSlowEaseIn,
reverseCurve: Curves.fastOutSlowIn);
return ScaleTransition(
alignment: Alignment.center,
scale: animation,
child: child,
);
},
);
}
对我来说,我使用 OnGenerateRoute 如下:
class RouteGenerator {
static Route<dynamic> generatedRoute(RouteSettings settings) {
switch (settings.name) {
case MainScreen.routeName:
return SizeTransitionAnimation(
page: MainScreen(),
);
case ProductDetailsScreen.routeName:
return SizeTransitionAnimation(
page: ProductDetailsScreen(),
);
default:
return _errorRoute();
}
}