Material组件库中提供了一个MaterialPageRoute组件,它可以使用和平台风格一致的路由切换动画,如在iOS上会左右滑动切换,而在Android上会上下滑动切换
1. CupertinoPageRoute
CupertinoPageRoute是Cupertino组件库提供的iOS风格的路由切换组件
如果在Android上也想使用左右切换风格,可以使用CupertinoPageRoute。
Navigator.push(context, CupertinoPageRoute(
builder: (context)=>PageB(),
));
示例
class MSCupertinoPageRouteDemo extends StatelessWidget {
const MSCupertinoPageRouteDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("CupertinoPage")),
body: Container(
child: Text("Home", textScaleFactor: 2.0),
alignment: Alignment.center,
color: Colors.red,
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.pets),
onPressed: () {
Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
return MSRoutePageDetail();
}));
},
),
);
}
}
class MSRoutePageDetail extends StatelessWidget {
const MSRoutePageDetail({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Detail"),
),
body: Container(
child: Text("Detail", textScaleFactor: 2.0),
alignment: Alignment.center,
color: Colors.amber,
),
);
}
}
2. PageRouteBuilder
PageRouteBuilder可以自定义路由切换动画
例如我们想以渐隐渐入动画来实现路由过渡
Navigator.of(context).push(
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 2000),
pageBuilder: (ctx, anim1, anim2) {
return FadeTransition(
opacity: anim1,
child: MSRoutePageDetail(),
);
},
),
);
class MSPageRouteBuilderDemo extends StatelessWidget {
const MSPageRouteBuilderDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("PageRouteBuilderDemo")),
body: Container(
alignment: Alignment(0, 0),
color: Colors.red,
child: Text(
"Home",
textScaleFactor: 2.0,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.pets),
onPressed: () {
Navigator.of(context).push(
PageRouteBuilder(
transitionDuration: Duration(milliseconds: 2000),
pageBuilder: (ctx, anim1, anim2) {
return FadeTransition(
opacity: anim1,
child: MSRoutePageDetail(),
);
},
),
);
},
),
);
}
}
class MSRoutePageDetail extends StatelessWidget {
const MSRoutePageDetail({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Detail"),
),
body: Container(
child: Text("Detail", textScaleFactor: 2.0),
alignment: Alignment.center,
color: Colors.amber,
),
);
}
}
我们可以看到pageBuilder 有一个animation参数,这是Flutter路由管理器提供的,在路由切换时pageBuilder在每个动画帧都会被回调,因此我们可以通过animation对象来自定义过渡动画。
3. 自定义 PageRoute
无论是MaterialPageRoute、CupertinoPageRoute,还是PageRouteBuilder,它们都继承自PageRoute类,而PageRouteBuilder其实只是PageRoute的一个包装,我们可以直接继承PageRoute类来实现自定义路由。
class MSFadeRoute extends PageRoute {
MSFadeRoute(
{required this.builder,
this.transitionDuration = const Duration(microseconds: 300),
this.maintainState = true,
this.barrierColor,
this.barrierLabel});
final WidgetBuilder builder;
@override
final Color? barrierColor;
@override
final String? barrierLabel;
@override
final bool maintainState;
@override
final Duration transitionDuration;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return builder(context);
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(opacity: animation, child: builder(context));
}
}
使用MSFadeRoute
Navigator.of(context).push(
MSFadeRoute(
builder: (ctx) {
return MSRoutePageDetail();
},
transitionDuration: Duration(seconds: 2),
),
);
完整代码
class MSFadeRouteDemo extends StatelessWidget {
const MSFadeRouteDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("MSFadeRouteDemo"),
),
body: Container(
alignment: Alignment(0, 0),
color: Colors.red,
child: Text(
"Home",
textScaleFactor: 2.0,
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.pets),
onPressed: () {
Navigator.of(context).push(
MSFadeRoute(
builder: (ctx) {
return MSRoutePageDetail();
},
transitionDuration: Duration(seconds: 2),
),
);
},
),
);
}
}
class MSRoutePageDetail extends StatelessWidget {
const MSRoutePageDetail({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Detail"),
),
body: Container(
child: Text("Detail", textScaleFactor: 2.0),
alignment: Alignment.center,
color: Colors.amber,
),
);
}
}
class MSFadeRoute extends PageRoute {
MSFadeRoute(
{required this.builder,
this.transitionDuration = const Duration(microseconds: 300),
this.maintainState = true,
this.barrierColor,
this.barrierLabel});
final WidgetBuilder builder;
@override
final Color? barrierColor;
@override
final String? barrierLabel;
@override
final bool maintainState;
@override
final Duration transitionDuration;
@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return builder(context);
}
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
return FadeTransition(opacity: animation, child: builder(context));
}
}
虽然PageRouteBuilder 和 MSFadeRoute都可以实现自定义切换动画,但实际使用时应优先考虑使用PageRouteBuilder,这样无需定义一个新的路由类,使用起来会比较方便。但是有些时候PageRouteBuilder是不能满足需求的,例如在应用过渡动画时我们需要读取当前路由的一些属性,这时就只能通过继承PageRoute的方式了。
举个例子,假如我们只想在打开新路由时应用动画,而在返回时不使用动画,那么我们在构建过渡动画时就必须判断当前路由isActive属性是否为true,代码如下
@override
Widget buildTransitions(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation, Widget child) {
//当前路由被激活,是打开新路由
if(isActive) {
return FadeTransition(
opacity: animation,
child: builder(context),
);
}else{
//是返回,则不应用过渡动画
return Padding(padding: EdgeInsets.zero);
}
}
https://book.flutterchina.club/chapter9/route_transition.html