Flutter: Keep BottomNavigationBar When Push to New Screen with Navigator

試著忘記壹切 提交于 2019-12-09 04:21:59

问题


In iOS, we have a UITabBarController which stays permanently at the bottom of the screen when we push to a new ViewController.

In Flutter, we have a bottomNavigationBar of a Scaffold. However, unlike iOS, when we Navigator.push to a new screen, this bottomNavigationBar disappears.

In my app, I want to fulfil this requirement: Home screen has a bottomNavigationBar with 2 items (a & b) presenting screen A & B. By default, screen A is displayed. Inside screen A, there is a button. Tap that button, Navigator.push to screen C. Now in screen C, we can still see the bottomNavigationBar. Tap item b, I go to screen B. Now in screen B, tap item a in the bottomNavigationBar, I go back to screen C (not A, A is currently below C in the navigation hierarchy).

How can I do this? Thanks, guys.

Edit: I'm including some pictures for demonstration:

Screen A Screen A

Tap Go to C button, push to screen C Screen C

Tap Right item inside bottom navigation bar, go to screen B Screen B


回答1:


tl;dr: Use CupertinoTabBar with CupertinoTabScaffold

The problem is not in Flutter but in UX just like Rémi Rousselet has mentioned.

It turned out Material Design doesn't recommend sub-pages in the hierarchy to access the Bottom navigation bar.

However, iOS Human Interface Guide recommend this. So, to use this feature, I had to adapt Cupertino widgets instead of Material ones. Specifically, in main, return a WidgetsApp/MaterialApp which contains a CupertinoTabScaffold. Implement the tab bar with a CupertinoTabBar and each screen is a CupertinoTabView.




回答2:


You need to create MaterialApp with routes and make BottomNavigationBar a sibling of it. Then use MaterialApp.navigatorKey which you pass to BottomNavigationBar to do navigation.

https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5




回答3:


You could actually place a placeholder inside body so the structure like this

- AppBar
- body (dynamic content from placeholder)
- BottomNavigationBar

Then you would have another class as a placeholder So each time you tap on the BottomNavigationBar it will refresh content of the body

One example I found is here https://willowtreeapps.com/ideas/how-to-use-flutter-to-build-an-app-with-bottom-navigation

and here but a litte too complex and not working for me https://medium.com/@swav.kulinski/flutter-navigating-off-the-charts-e118562a36a5

and this https://medium.com/coding-with-flutter/flutter-case-study-multiple-navigators-with-bottomnavigationbar-90eb6caa6dbf




回答4:


I think the #right way of doing this would be to have the BottomNavigationBar wrapped in a Hero in both cases with the same tag. This way, when the animation between pages happens they would be excluded.

This is as brief as an example as I could make, but I'd highly recommend cleaning it up i.e. passing the hero string in, using widgets rather than a huge block of build, making your own widget for BottomNavigationBar.

Note that during the hero transition it does overflow by 0.0000191 pixels on my phone at least, but in release mode that shouldn't be an issue I don't think.

import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(
      home: new Builder(
        builder: (context) => new Scaffold(
              bottomNavigationBar: new Hero(
                tag: "bottomNavigationBar",
                child: new BottomNavigationBar(items: [
                  new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
                  new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
                ]),
              ),
              body: new SafeArea(
                child: new Container(
                  constraints: new BoxConstraints.expand(),
                  color: Colors.green,
                  child: new Column(
                    children: <Widget>[
                      new RaisedButton(
                          child: new Text("Press me"),
                          onPressed: () {
                            Navigator.push(
                                context,
                                new MaterialPageRoute(
                                    builder: (context) => new Scaffold(
                                          bottomNavigationBar: new Hero(
                                            tag: "bottomNavigationBar",
                                            child: new BottomNavigationBar(items: [
                                              new BottomNavigationBarItem(icon: new Icon(Icons.home), title: new Text("Home")),
                                              new BottomNavigationBarItem(icon: new Icon(Icons.ac_unit), title: new Text("AC Unit"))
                                            ]),
                                          ),
                                          body: new SafeArea(
                                            child: new Container(
                                              constraints:
                                                  new BoxConstraints.expand(),
                                              color: Colors.red,
                                              child: new Column(
                                                children: <Widget>[
                                                  new RaisedButton(
                                                    onPressed: () =>
                                                        Navigator.pop(context),
                                                    child: new Text("Back"),
                                                  )
                                                ],
                                              ),
                                            ),
                                          ),
                                        )));
                          })
                    ],
                  ),
                ),
              ),
            ),
      ),
    ));

I don't know how well the hero system handles multiple heroes etc, and if you say wanted to animate the navigation bar this might not work overly well.

There is another way of doing this which would allow you to animate the bottom navigation bar; it's actually a question that has already been answered though: Flutter: Hero transition + widget animation at the same time?




回答5:


Another way to achieve this (though not good practice) is to nest a material app in the body of your scaffold. And handle all "sub-navigation" there.

So, your hierarchy will look like this

Material App
  - home
     - Scaffold
       - body
         - Material App
              - Scaffold
                  - AppBar
                  - body
                  ...
         - routes (internal)
       - bottomNavigationBar
  - routes (external)

I've tried this and it works perfectly. Unfortunately I can't post the source code now.




回答6:


You can create Navigator widget in a Stack widget to use BottomNavigationBar with tabs' inner navigation. You can use WillPopScope to handle Android's back button to pop inner screens of tab. Also, double tap bottom navigation item to pop all inner screens of a tab.

I've created a Sample app for this.

Hope this help!




回答7:


I created a small, super easy to use package that let you do that effect CustomNavigator And wrote a tutorial about it on Medium you can find it here



来源:https://stackoverflow.com/questions/49628510/flutter-keep-bottomnavigationbar-when-push-to-new-screen-with-navigator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!