Flutter:带有嵌套导航的底部导航栏和更改选项卡时恢复根页面

Posted

技术标签:

【中文标题】Flutter:带有嵌套导航的底部导航栏和更改选项卡时恢复根页面【英文标题】:Flutter: Bottom Navigation bar with nested navigation and Restoring the Root page when tabs are changed 【发布时间】:2021-08-06 04:02:55 【问题描述】:

我是 Flutter 开发的新手。而且我已经通过多个教程来了解底部导航栏。

我已经尝试过这些教程,但我无法达到我的要求。 我遵循的教程:

    https://codewithandrea.com/articles/multiple-navigators-bottom-navigation-bar/ https://medium.com/flutter/getting-to-the-bottom-of-navigation-in-flutter-b3e440b9386 https://medium.com/@theboringdeveloper/common-bottom-navigation-bar-flutter-e3693305d2d

我个人喜欢第一个教程,因为有嵌套路由。

信息:

我有 3 个标签的底部导航:主页、日历、个人资料。

主页标签有一个屏幕:Screen2日历有一个屏幕:Screen3个人资料有一个屏幕:Screen4

问题

我的底部导航栏保持屏幕状态(这很好)。

主屏幕有一个打开Screen2的按钮。当用户点击时,它会推动 Screen2。当用户单击日历(选项卡)时,用户会看到日历屏幕。现在,用户再次单击主页按钮(选项卡),用户看到 Screen2。因为它是那条路线(家)的一部分。他应该只看到主屏幕的位置。

我只想重置它。基本上主屏幕应该推送主屏幕并弹出主页的所有子项。切换标签时。

代码:

main.dart

class MyApp extends StatelessWidget 
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) 
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MainScreen(),
    );
  

main_screen.dart

class MainScreen extends StatefulWidget 
  MainScreen(Key key) : super(key: key);

  @override
  _MainScreenState createState() => _MainScreenState();


class _MainScreenState extends State<MainScreen> 
  int _selectedIndex = 0;

  List<GlobalKey<NavigatorState>> _navigatorKeys = [
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>(),
    GlobalKey<NavigatorState>()
  ];

  List<Widget> _widgetOptions = <Widget>[
    HomePage(),
    CalendarPage(),
    ProfilePage(),
  ];

  Map<String, WidgetBuilder> _routeBuilders(BuildContext context, int index) 
    return 
      '/': (context) 
        return [
          HomePage(),
          CalendarPage(),
          ProfilePage(),
        ].elementAt(index);
      ,
    ;
  


  Widget _buildOffstageNavigator(int index) 
    var routeBuilders = _routeBuilders(context, index);

    return Offstage(
      offstage: _selectedIndex != index,
      child: Navigator(
        key: _navigatorKeys[index],
        onGenerateRoute: (routeSettings) 
          return MaterialPageRoute(
            builder: (context) => routeBuilders[routeSettings.name](context),
          ); 
        ,
      ),
    );
  

  @override
  Widget build(BuildContext context) 
    return WillPopScope(
      onWillPop: () async 
        final isFirstRouteInCurrentTab =
        !await _navigatorKeys[_selectedIndex].currentState.maybePop();

        // let system handle back button if we're on the first route
        return isFirstRouteInCurrentTab;
      ,
      child: Scaffold(
        backgroundColor: Colors.white,
        body: SafeArea(
          child: Stack(
            children: [
              _buildOffstageNavigator(0),
              _buildOffstageNavigator(1),
              _buildOffstageNavigator(2),
            ],
          ),
        ),
        bottomNavigationBar: BottomNavigationBar(
          currentIndex: _selectedIndex,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          items: [
            BottomNavigationBarItem(
              icon: Icon(
                Feather.home,
                color: Colors.grey[300],
              ),
              label: 'HOME',
              activeIcon: Icon(
                Feather.home,
                color: Colors.purple[300],
              ),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                FontAwesome.calendar,
                color: Colors.grey[300],
              ),
              label: 'CALENDAR',
              activeIcon: Icon(
                FontAwesome.calendar,
                color: Colors.purple[300],
              ),
            ),
            BottomNavigationBarItem(
              icon: Icon(
                EvilIcons.user,
                color: Colors.grey[300],
                size: 36,
              ),
              label: 'PROFILE',
              activeIcon: Icon(
                EvilIcons.user,
                color: Colors.purple[300],
                size: 36,
              ),
            ),
          ],
          onTap: (index) 
            setState(() 
              _selectedIndex = index;
            );
          ,
        ),
      ),
    );
  

home_page.dart

class HomePage extends StatefulWidget 
  HomePage();

  @override
  _HomePageState createState() => _HomePageState();


class _HomePageState extends State<HomePage> 
  @override
  Widget build(BuildContext context) 
    return Container(
        color: Colors.lightBlueAccent,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Container(
              child: Text(
                'Screen 1',
                style: TextStyle(color: Colors.white, fontSize: 20),
              ),
              margin: EdgeInsets.all(16),
            ),
            FlatButton(
              onPressed: () 
                // Navigator.push(context, MaterialPageRoute(
                //   builder: (context) => Screen2()
                // ));

                Navigator.push(context, PageRouteBuilder(pageBuilder: (_,__,___) => Screen2()));
              ,
              child: Text('Go to next screen'),
              color: Colors.white,
            ),
          ],
        ));
  


calendar_page.dart

class CalendarPage extends StatefulWidget 
  CalendarPage(Key key) : super(key: key);

  @override
  _CalendarPageState createState() => _CalendarPageState();


class _CalendarPageState extends State<CalendarPage> 
  @override
  Widget build(BuildContext context) 
    return Container(
      color: Colors.red,
      child: Center(
        child: FlatButton(
          onPressed: ()
            Navigator.push(context, MaterialPageRoute(
                builder: (context) => Screen3()
            ));
          ,
          child: Text('Go to next screen'),
          color: Colors.white,
        ),
      ),
    );
  

如果有人能指出我的方向,我将不胜感激。提前致谢。

要求: 有选项卡,每个选项卡将各自的屏幕(ScreenX-> ScreenY-> ScreenN)。当切换选项卡时,它应该弹出标签的所有子项。我希望这可以理解(对不起,我的英语不好)。

我错过了什么?

【问题讨论】:

我被困在同一个问题上,并且涉猎无济于事。如果您很幸运并找到了解决方案,请分享,它将对我有所帮助:) 是的,我将通过 EOD 或明天 EOD 分享解决方案。如果我没有,请再次评论,以便我得到提醒 如果有帮助请告诉我 【参考方案1】:

所以逻辑是如果我从主屏幕(选项卡)移到任何其他选项卡。我应该清除堆栈(不是指小部件)。

假设您正在学习第一个教程。

有 3 个选项卡。 首页日历个人资料

现在我从主屏幕添加了“Screen2”。所以,现在我的 currentTab 是 “首页”。如果我点击 Calendar 选项卡,我 selectedTtab 是“Calendar”。

我会弹出当前选项卡中的所有内容,直到遇到第一条路线。完成后,我将设置状态。

代码:


void _selectTab(TabItem tabItem) 
    if (tabItem == _currentTab) 
      _navigatorKeys[tabItem]?.currentState?.popUntil((route) => route.isFirst);
     else 
      //! Added logic to Pop everything from Home tab, if any other tab is clicked
      if (_currentTab == TabItem.HOME) 
        _navigatorKeys[_currentTab]
            ?.currentState
            ?.popUntil((route) => route.isFirst);
      
      setState(() => _currentTab = tabItem);
    
  

我从底部导航调用这个方法。

bottomNavigationBar: BottomNavigation(
          currentTab: _currentTab,
          onSelectTab: _selectTab,
        ),

希望这会有所帮助。让我知道这是否足够。

【讨论】:

抱歉回复慢,忙于庆祝活动。我将在周末对此进行测试并更新您,感谢您分享此解决方案。希望它有效? :) 嘿 Akshay,我终于可以开始编写代码并检查了。您的更改有助于管理非常棒的状态。但我的问题还有第二部分。如何退出持久选项卡视图方法?例如,从menu 屏幕到login 屏幕。

以上是关于Flutter:带有嵌套导航的底部导航栏和更改选项卡时恢复根页面的主要内容,如果未能解决你的问题,请参考以下文章

在 Flutter 中使用带有 WillPopScope 的嵌套导航器

使用底部应用栏标签导航 - 颤动

单击底部导航栏时关闭 Flutter 抽屉

Flutter系统导航栏和状态栏颜色[重复]

带有 PageView 的 Flutter 底部导航栏

带有 url 更新和超链接支持的 Flutter web 底部导航栏