简体   繁体   English

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

[英]Flutter: Bottom Navigation bar with nested navigation and Restoring the Root page when tabs are changed

I am new to Flutter development.我是 Flutter 开发的新手。 And I have been going through multiple tutorials to understand the Bottom Navigation bar.我已经通过多个教程来了解底部导航栏。

I have tried these tutorials but I am not able to achieve the requirement that I have.我已经尝试过这些教程,但我无法达到我的要求。 Tutorials I have followed:我遵循的教程:

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

I personally liked the 1st tutorial because there are nested routes.我个人喜欢第一个教程,因为有嵌套路由。

Information:信息:

I have bottom navigation with 3 tabs: Home, Calendar, Profile.我有 3 个选项卡的底部导航:主页、日历、个人资料。

Home tabs has a screen: Screen2 .主页选项卡有一个屏幕: Screen2 Calendar has a screen: Screen3 .日历有一个屏幕: Screen3 Profile has a screen: Screen4配置文件有一个屏幕: Screen4

Problem :问题

My bottom navigation bar persisting the state of the screen(which is good).我的底部导航栏保留了屏幕的 state(这很好)。

Home screen has a button which opens Screen2 .主屏幕有一个打开Screen2的按钮。 When user clicks, it pushes the Screen2.当用户点击时,它会推动 Screen2。 And when user clicks on Calendar (tab) user sees Calendar screen.当用户单击日历(选项卡)时,用户会看到日历屏幕。 Now, user again clicks on Home button(tab), user sees the Screen2.现在,用户再次单击主页按钮(选项卡),用户看到 Screen2。 Because It was part of that route(Home).因为它是那条路线(家)的一部分。 Where he should see Home screen only.他应该只看到主屏幕的地方。

And I just want to reset it.我只想重置它。 Basically Home screen should push Home screen and pop all the children of Home page.基本上主屏幕应该推送主屏幕并弹出主页的所有子项。 When tabs are switched.切换选项卡时。

Code:代码:

main.dart 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 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 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 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,
        ),
      ),
    );
  }
}

I would really appreciate if anyone could point me to direction.如果有人能指出我的方向,我将不胜感激。 Thanks in Advance.提前致谢。

Requirement : There are tabs, each tab will respective screens(ScreenX-> ScreenY-> ScreenN).要求:有标签,每个标签都有各自的屏幕(ScreenX-> ScreenY-> ScreenN)。 And when tab is switched it should pop all the children of the tabs. And when tab is switched it should pop all the children of the tabs. I hope this understandable(Sorry, my English is not good).我希望这可以理解(对不起,我的英语不好)。

What am I missing here?我在这里想念什么?

So the logic is if I move away from Home screen(tab) to any other tab.所以逻辑是如果我从主屏幕(选项卡)移到任何其他选项卡。 I should clear the stack(not referring to the widget).我应该清除堆栈(不是指小部件)。

Assuming you are following 1st tutorial.假设您正在学习第一个教程。

There are 3 tabs.有 3 个选项卡。 Home , Calendar and Profile .主页日历个人资料

Now from Home screen I added " Screen2 ".现在从主屏幕我添加了“ Screen2 ”。 So, right now my currentTab is " Home ".所以,现在我的 currentTab 是“ Home ”。 If I click Calendar tab my selectedTtab is " Calendar ".如果我单击日历选项卡,我 selectedTtab 是“日历”。

I will pop everything from my current tab until the first route is met.我将从当前选项卡中弹出所有内容,直到遇到第一条路线。 Once this is done I will set the state.完成后,我将设置 state。

Code:代码:


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);
    }
  }

And I am calling this method from Bottom navigation.我从底部导航调用这个方法。

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

Hope this helps.希望这可以帮助。 Let me know if this is sufficient.让我知道这是否足够。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM