flutter中BottomSheet里的路由怎么跳转,附动画效果
Flutter实现BottomSheet内嵌路由动画效果 摘要:在Flutter仿写微信搜索功能时,遇到BottomSheet内页面跳转的动画需求。传统使用多次showModalBottomSheet的方式存在体验问题。本文提出优雅解决方案:通过PageRouteBuilder封装共享的BottomSheet导航器(SharedBottomSheetNavigator),利用Navigator组件
·
背景
我在用flutter仿写微信APP时,遇到一个问题,那就是搜索功能里有一个页面设置按钮,
- 点击页面设置,会从底部弹出一个页面配置弹框:BottomSheet弹框层(该层也是一个路由)
- 点击
搜索指定内容Tile,会跳转到一个页面。包含各种开关的配置 - 点击配置页的返回按钮,又会返回到页面配置。

为方便描述,我们将页面描述为别名
- 页面A:页面配置
- 页面B:开关配置详情
我们发现
- 页面A和页面B,都在BottomSheet层级中,且Bottom层级高度固定;
- 从页面A到页面B,有从右到左的动画效果
- 点击页面B的返回按钮,有从左到右然后消失的动画效果
思路
我们都知道,flutter中从一个路由跳转到另一个路由,有两种常用的方式
- Navigator.push:直接跳转到路由的组件页面
- Navigator.pushNamed:命名路由,更优雅
分析
但是上面的两种方式,都是基于页面级别的路由,如果还用Navigator路由跳转的方式,那么会把页面全屏化。
而现在需要把路由放在showModalBottomSheet中,有人想这么做:
- 点击
页面设置按钮时,我调用showModalBottomSheet显示页面A; - 点击
搜索指定内容Tile时,我再次调用showModalBottomSheet显示页面B; - 把高度设置成一样的就行了;
上述方法能实现功能,但是体验会大打折扣,因为:
- 2次调用
showModalBottomSheet虽然不会有太大的性能问题,但是不优雅; - 页面A、B之间的跳转与返回,无动画效果,每次都是从底部弹出
BottomSheet层; - 且每次AB页面切换时,BottomSheet层的顶部圆角,会从无到有,过渡非常不自然;
解决方案
需要使用 PageRouteBuilder 结合动画来实现上述功能,把页面A、页面B放在封装的SharedBottomSheetNavigator组件中,默认显示页面A,跳转时指向页面B,且使用自定义的动画效果,页面B返回页面A同理,其代码如下
import 'package:flutter/material.dart';
import './page_setting.dart'; // 页面A
import './switch_list_page.dart'; // 页面B
class SharedBottomSheetNavigator extends StatelessWidget {
const SharedBottomSheetNavigator({super.key});
static void show(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
constraints: BoxConstraints(maxHeight: 600),
builder: (context) {
return ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
child: ColoredBox(
color: Colors.white,
child: Navigator(
key: GlobalKey<NavigatorState>(),
initialRoute: '/',
onGenerateRoute: (settings) {
return PageRouteBuilder(
settings: settings,
pageBuilder: (context, animation, secondaryAnimation) {
switch (settings.name) {
case '/':
return const PageSetting();
case '/switch':
return const MultipleSwitchList();
default:
return const PageSetting();
}
},
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(1.0, 0.0);
const end = Offset.zero;
const curve = Curves.ease;
final tween = Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
);
},
),
),
);
},
);
}
Widget build(BuildContext context) {
return const SizedBox.shrink();
}
}
点击页面A进入页面B时,只需要跳转这个特定的路由即可
Navigator.pushNamed(context, '/switch');
其最终效果如下,有兴趣的同学可以试试
更多推荐

所有评论(0)