走进Navigator 2.0
RoutePath
RouteInformationParser类
创建页面
RouteDelegate类
使用
调用流程
封装导航框架
hi_navigator.dart
typedef RouteChangeListener(RouteStatusInfo current, RouteStatusInfo pre);
///创建页面
pageWrap(Widget child) {
return MaterialPage(key: ValueKey(child.hashCode), child: child);
}
///获取routeStatus在页面栈中的位置
int getPageIndex(List<MaterialPage> pages, RouteStatus routeStatus) {
for (int i = 0; i < pages.length; i++) {
MaterialPage page = pages[i];
if (getStatus(page) == routeStatus) {
return i;
}
}
return -1;
}
///自定义路由封装,路由状态
enum RouteStatus { login, registration, home, detail, unknown }
///获取page对应的RouteStatus
RouteStatus getStatus(MaterialPage page) {
if (page.child is LoginPage) {
return RouteStatus.login;
} else if (page.child is RegistrationPage) {
return RouteStatus.registration;
} else if (page.child is BottomNavigator) {
return RouteStatus.home;
} else if (page.child is VideoDetailPage) {
return RouteStatus.detail;
} else {
return RouteStatus.unknown;
}
}
///路由信息
class RouteStatusInfo {
final RouteStatus routeStatus;
final Widget page;
RouteStatusInfo(this.routeStatus, this.page);
}
///监听路由页面跳转
///感知当前页面是否压后台
class HiNavigator extends _RouteJumpListener {
static HiNavigator _instance;
RouteJumpListener _routeJump;
List<RouteChangeListener> _listeners = [];
RouteStatusInfo _current;
//首页底部tab
RouteStatusInfo _bottomTab;
HiNavigator._();
static HiNavigator getInstance() {
if (_instance == null) {
_instance = HiNavigator._();
}
return _instance;
}
///首页底部tab切换监听
void onBottomTabChange(int index, Widget page) {
_bottomTab = RouteStatusInfo(RouteStatus.home, page);
_notify(_bottomTab);
}
///注册路由跳转逻辑
void registerRouteJump(RouteJumpListener routeJumpListener) {
this._routeJump = routeJumpListener;
}
///监听路由页面跳转
void addListener(RouteChangeListener listener) {
if (!_listeners.contains(listener)) {
_listeners.add(listener);
}
}
///移除监听
void removeListener(RouteChangeListener listener) {
_listeners.remove(listener);
}
@override
void onJumpTo(RouteStatus routeStatus, {Map args}) {
_routeJump.onJumpTo(routeStatus, args: args);
}
///通知路由页面变化
void notify(List<MaterialPage> currentPages, List<MaterialPage> prePages) {
if (currentPages == prePages) return;
var current =
RouteStatusInfo(getStatus(currentPages.last), currentPages.last.child);
_notify(current);
}
void _notify(RouteStatusInfo current) {
if (current.page is BottomNavigator && _bottomTab != null) {
//如果打开的是首页,则明确到首页具体的tab
current = _bottomTab;
}
print('hi_navigator:current:${current.page}');
print('hi_navigator:pre:${_current?.page}');
_listeners.forEach((listener) {
listener(current, _current);
});
_current = current;
}
}
///抽象类供HiNavigator实现
abstract class _RouteJumpListener {
void onJumpTo(RouteStatus routeStatus, {Map args});
}
typedef OnJumpTo = void Function(RouteStatus routeStatus, {Map args});
///定义路由跳转逻辑要实现的功能
class RouteJumpListener {
final OnJumpTo onJumpTo;
RouteJumpListener({this.onJumpTo});
}
main.dart
class BiliRouteDelegate extends RouterDelegate<BiliRoutePath>
with ChangeNotifier, PopNavigatorRouterDelegateMixin<BiliRoutePath> {
final GlobalKey<NavigatorState> navigatorKey;
//为Navigator设置一个key,必要的时候可以通过navigatorKey.currentState来获取到NavigatorState对象
BiliRouteDelegate() : navigatorKey = GlobalKey<NavigatorState>() {
//实现路由跳转逻辑
HiNavigator.getInstance().registerRouteJump(
RouteJumpListener(onJumpTo: (RouteStatus routeStatus, {Map args}) {
_routeStatus = routeStatus;
if (routeStatus == RouteStatus.detail) {
this.videoModel = args['videoMo'];
}
notifyListeners();
}));
/*
//设置网络错误拦截器
HiNet.getInstance().setErrorInterceptor((error) {
if (error is NeedLogin) {
//清空失效的登录令牌
HiCache.getInstance().setString(LoginDao.BOARDING_PASS, null);
//拉起登录
HiNavigator.getInstance().onJumpTo(RouteStatus.login);
}
});
*/
}
RouteStatus _routeStatus = RouteStatus.home;
List<MaterialPage> pages = [];
VideoModel videoModel;
@override
Widget build(BuildContext context) {
var index = getPageIndex(pages, routeStatus);
List<MaterialPage> tempPages = pages;
if (index != -1) {
//要打开的页面在栈中已存在,则将该页面和它上面的所有页面进行出栈
//tips 具体规则可以根据需要进行调整,这里要求栈中只允许有一个同样的页面的实例
tempPages = tempPages.sublist(0, index);
}
var page;
if (routeStatus == RouteStatus.home) {
//跳转首页时将栈中其它页面进行出栈,因为首页不可回退
pages.clear();
page = pageWrap(BottomNavigator());
} else if (routeStatus == RouteStatus.detail) {
page = pageWrap(VideoDetailPage(videoModel));
} else if (routeStatus == RouteStatus.registration) {
page = pageWrap(RegistrationPage());
} else if (routeStatus == RouteStatus.login) {
page = pageWrap(LoginPage());
}
//重新创建一个数组,否则pages因引用没有改变路由不会生效
tempPages = [...tempPages, page];
//通知路由发生变化
HiNavigator.getInstance().notify(tempPages, pages);
pages = tempPages;
return WillPopScope(
//fix Android物理返回键,无法返回上一页问题@https://github.com/flutter/flutter/issues/66349
onWillPop: () async => !await navigatorKey.currentState.maybePop(),
child: Navigator(
key: navigatorKey,
pages: pages,
onPopPage: (route, result) {
if (route.settings is MaterialPage) {
//登录页未登录返回拦截
if ((route.settings as MaterialPage).child is LoginPage) {
if (!hasLogin) {
showWarnToast("请先登录");
return false;
}
}
}
//执行返回操作
if (!route.didPop(result)) {
return false;
}
var tempPages = [...pages];
pages.removeLast();
//通知路由发生变化
HiNavigator.getInstance().noify(pages, tempPages);
return true;
},
),
);
}
RouteStatus get routeStatus {
if (_routeStatus != RouteStatus.registration && !hasLogin) {
return _routeStatus = RouteStatus.login;
} else if (videoModel != null) {
return _routeStatus = RouteStatus.detail;
} else {
return _routeStatus;
}
}
bool get hasLogin => LoginDao.getBoardingPass() != null;
@override
Future<void> setNewRoutePath(BiliRoutePath path) async {}
}
///定义路由数据,path
class BiliRoutePath {
final String location;
BiliRoutePath.home() : location = "/";
BiliRoutePath.detail() : location = "/detail";
}
home_page.dart
class HomePage extends StatefulWidget {
const HomePage({Key key}) : super(key: key);
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage>
with AutomaticKeepAliveClientMixin, TickerProviderStateMixin {
var listener;
TabController _controller;
var tabs = ["推荐", "热门", "追播", "影视", "搞笑", "日常", "综合", "手机游戏", "短片·手书·配音"];
@override
void initState() {
super.initState();
_controller = TabController(length: tabs.length, vsync: this);
HiNavigator.getInstance().addListener(this.listener = (current, pre) {
print('home:current:${current.page}');
print('home:pre:${pre.page}');
if (widget == current.page || current.page is HomePage) {
print('打开了首页:onResume');
} else if (widget == pre?.page || pre?.page is HomePage) {
print('首页:onPause');
}
});
}
@override
void dispose() {
HiNavigator.getInstance().removeListener(this.listener);
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
body: Column(
children: [
Container(
color: Colors.white,
padding: EdgeInsets.only(top: 30),
child: _tabBar(),
),
Flexible(
child: TabBarView(
controller: _controller,
children: tabs.map((tab) {
return HomeTabPage(name: tab);
}).toList()))
],
),
);
}
@override
bool get wantKeepAlive => true;
///自定义顶部tab
_tabBar() {
return TabBar(
controller: _controller,
isScrollable: true,
labelColor: Colors.black,
indicator: UnderlineIndicator(
strokeCap: StrokeCap.round,
borderSide: BorderSide(color: primary, width: 3),
insets: EdgeInsets.only(left: 15, right: 15)),
tabs: tabs.map<Tab>((tab) {
return Tab(
child: Padding(
padding: EdgeInsets.only(left: 5, right: 5),
child: Text(
tab,
style: TextStyle(fontSize: 16),
),
));
}).toList());
}
}
class HomeTabPage extends StatefulWidget {
final String name;
const HomeTabPage({Key key, this.name}) : super(key: key);
@override
_HomeTabPageState createState() => _HomeTabPageState();
}
class _HomeTabPageState extends State<HomeTabPage> {
@override
Widget build(BuildContext context) {
return Container(
child: Text(widget.name),
);
}
}
底部导航:PageView + BottomNavigationBar
///底部导航
class BottomNavigator extends StatefulWidget {
@override
_BottomNavigatorState createState() => _BottomNavigatorState();
}
class _BottomNavigatorState extends State<BottomNavigator> {
final _defaultColor = Colors.grey;
final _activeColor = primary;
int _currentIndex = 0;
static int initialPage = 0;
final PageController _controller = PageController(initialPage: initialPage);
List<Widget> _pages;
bool _hasBuild = false;
@override
Widget build(BuildContext context) {
_pages = [HomePage(), RankingPage(), FavoritePage(), ProfilePage()];
if (!_hasBuild) {
//页面第一次打开时通知打开的是那个tab
HiNavigator.getInstance()
.onBottomTabChange(initialPage, _pages[initialPage]);
_hasBuild = true;
}
return Scaffold(
body: PageView(
controller: _controller,
children: _pages,
onPageChanged: (index) => _onJumpTo(index, pageChange: true),
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) => _onJumpTo(index),
type: BottomNavigationBarType.fixed,
selectedItemColor: _activeColor,
items: [
_bottomItem('首页', Icons.home, 0),
_bottomItem('排行', Icons.local_fire_department, 1),
_bottomItem('收藏', Icons.favorite, 2),
_bottomItem('我的', Icons.live_tv, 3),
],
),
);
}
_bottomItem(String title, IconData icon, int index) {
return BottomNavigationBarItem(
icon: Icon(icon, color: _defaultColor),
activeIcon: Icon(icon, color: _activeColor),
label: title);
}
void _onJumpTo(int index, {pageChange = false}) {
if (!pageChange) {
//让PageView展示对应tab
_controller.jumpToPage(index);
} else {
HiNavigator.getInstance().onBottomTabChange(index, _pages[index]);
}
setState(() {
//控制选中第一个tab
_currentIndex = index;
});
}
}