How to use BottomNavigationBar with Navigator?

54,582

Solution 1

int index = 0;

@override
Widget build(BuildContext context) {
  return new Scaffold(
    body: new Stack(
      children: <Widget>[
        new Offstage(
          offstage: index != 0,
          child: new TickerMode(
            enabled: index == 0,
            child: new MaterialApp(home: new YourLeftPage()),
          ),
        ),
        new Offstage(
          offstage: index != 1,
          child: new TickerMode(
            enabled: index == 1,
            child: new MaterialApp(home: new YourRightPage()),
          ),
        ),
      ],
    ),
    bottomNavigationBar: new BottomNavigationBar(
      currentIndex: index,
      onTap: (int index) { setState((){ this.index = index; }); },
      items: <BottomNavigationBarItem>[
        new BottomNavigationBarItem(
          icon: new Icon(Icons.home),
          title: new Text("Left"),
        ),
        new BottomNavigationBarItem(
          icon: new Icon(Icons.search),
          title: new Text("Right"),
        ),
      ],
    ),
  );
}

You should keep each page by Stack to keep their state. Offstage stops painting, TickerMode stops animation. MaterialApp includes Navigator.

Solution 2

Output:

enter image description here

Code:

int _index = 0;

@override
Widget build(BuildContext context) {
  Widget child;
  switch (_index) {
    case 0:
      child = FlutterLogo();
      break;
    case 1:
      child = FlutterLogo(colors: Colors.orange);
      break;
    case 2:
      child = FlutterLogo(colors: Colors.red);
      break;
  }

  return Scaffold(
    body: SizedBox.expand(child: child),
    bottomNavigationBar: BottomNavigationBar(
      onTap: (newIndex) => setState(() => _index = newIndex),
      currentIndex: _index,
      items: [
        BottomNavigationBarItem(icon: Icon(Icons.looks_one), title: Text("Blue")),
        BottomNavigationBarItem(icon: Icon(Icons.looks_two), title: Text("Orange")),
        BottomNavigationBarItem(icon: Icon(Icons.looks_3), title: Text("Red")),
      ],
    ),
  );
}

Solution 3

The Complete Example

First make a class MyBottomBarDemo

class MyBottomBarDemo extends StatefulWidget {
  @override
  _MyBottomBarDemoState createState() => new _MyBottomBarDemoState();
}

class _MyBottomBarDemoState extends State<MyBottomBarDemo> {
  int _pageIndex = 0;
  PageController _pageController;

  List<Widget> tabPages = [
    Screen1(),
    Screen2(),
    Screen3(),
  ];

  @override
  void initState(){
    super.initState();
    _pageController = PageController(initialPage: _pageIndex);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("BottomNavigationBar", style: TextStyle(color: Colors.white)),
        backgroundColor: Colors.deepPurple,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _pageIndex,
        onTap: onTabTapped,
        backgroundColor: Colors.white,
        items: <BottomNavigationBarItem>[
          BottomNavigationBarItem( icon: Icon(Icons.home), title: Text("Home")),
          BottomNavigationBarItem(icon: Icon(Icons.mail), title: Text("Messages")),
          BottomNavigationBarItem(icon: Icon(Icons.person), title: Text("Profile")),
        ],

      ),
      body: PageView(
        children: tabPages,
        onPageChanged: onPageChanged,
        controller: _pageController,
      ),
    );
  }
  void onPageChanged(int page) {
    setState(() {
      this._pageIndex = page;
    });
  }

  void onTabTapped(int index) {
    this._pageController.animateToPage(index,duration: const Duration(milliseconds: 500),curve: Curves.easeInOut);
  }
}

Then create a your screens

class Screen1 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
        color: Colors.green,
            child: Center(child: Text("Screen 1")),
    );
  }
}

class Screen2 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.yellow,
      child: Center(child: Text("Screen 2")),
    );
  }
}

class Screen3 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.cyan,
      child: Center(child: Text("Screen 3")),
    );
  }
}

enter image description here

Solution 4

Here is an example how you can use Navigator with BottomNavigationBar to navigate different screen.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  // This navigator state will be used to navigate different pages
  final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
  int _currentTabIndex = 0;

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute),
        bottomNavigationBar: _bottomNavigationBar(),
      ),
    );
  }

  Widget _bottomNavigationBar() {
    return BottomNavigationBar(
      type: BottomNavigationBarType.fixed,
      items: [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          title: Text("Home"),
        ),
        BottomNavigationBarItem(
            icon: Icon(Icons.account_circle), title: Text("Account")),
        BottomNavigationBarItem(
          icon: Icon(Icons.settings),
          title: Text("Settings"),
        )
      ],
      onTap: _onTap,
      currentIndex: _currentTabIndex,
    );
  }

  _onTap(int tabIndex) {
    switch (tabIndex) {
      case 0:
        _navigatorKey.currentState.pushReplacementNamed("Home");
        break;
      case 1:
        _navigatorKey.currentState.pushReplacementNamed("Account");
        break;
      case 2:
        _navigatorKey.currentState.pushReplacementNamed("Settings");
        break;
    }
    setState(() {
      _currentTabIndex = tabIndex;
    });
  }

  Route<dynamic> generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case "Account":
        return MaterialPageRoute(builder: (context) => Container(color: Colors.blue,child: Center(child: Text("Account"))));
      case "Settings":
        return MaterialPageRoute(builder: (context) => Container(color: Colors.green,child: Center(child: Text("Settings"))));
      default:
        return MaterialPageRoute(builder: (context) => Container(color: Colors.white,child: Center(child: Text("Home"))));
    }
  }
}

Solution 5

Here is example:

  int _currentIndex = 0;


  Route<Null> _getRoute(RouteSettings settings) {
    final initialSettings = new RouteSettings(
        name: settings.name,
        isInitialRoute: true);

    return new MaterialPageRoute<Null>(
        settings: initialSettings,
        builder: (context) =>
        new Scaffold(
          body: new Center(
              child: new Container(
                  height: 200.0,
                  width: 200.0,
                  child: new Column(children: <Widget>[
                    new Text(settings.name),
                    new FlatButton(onPressed: () =>
                        Navigator.of(context).pushNamed(
                            "${settings.name}/next"), child: new Text("push")),
                  ],
                  ))
          ),
          bottomNavigationBar: new BottomNavigationBar(
              currentIndex: _currentIndex,
              onTap: (value) {
                final routes = ["/list", "/map"];
                _currentIndex = value;
                Navigator.of(context).pushNamedAndRemoveUntil(
                    routes[value], (route) => false);
              },
              items: [
                new BottomNavigationBarItem(
                    icon: new Icon(Icons.list), title: new Text("List")),
                new BottomNavigationBarItem(
                    icon: new Icon(Icons.map), title: new Text("Map")),
              ]),
        ));
  }

  @override
  Widget build(BuildContext context) =>
      new MaterialApp(
        initialRoute: "/list",
        onGenerateRoute: _getRoute,
        theme: new ThemeData(
          primarySwatch: Colors.blue,
        ),
      );

You can set isInitialRoute to true and pass it to MaterialPageRoute. It will remove pop animation.

And to remove old routes you can use pushNamedAndRemoveUntil

Navigator.of(context).pushNamedAndRemoveUntil(routes[value], (route) => false);

To set current page you can have a variable in your state _currentIndex and assign it to BottomNavigationBar:

Share:
54,582

Related videos on Youtube

Paul
Author by

Paul

Updated on February 19, 2022

Comments

  • Paul
    Paul about 2 years

    The Flutter Gallery example of BottomNavigationBar uses a Stack of FadeTransitions in the body of the Scaffold.

    I feel it would be cleaner (and easier to animate) if we could switch pages by using a Navigator.

    Are there any examples of this?

  • Paul
    Paul almost 7 years
    Interesting. Is the BottomNavigationBar sticking around throughout navigation changes? I'll need it to also reflect the current page.
  • German Saprykin
    German Saprykin almost 7 years
    BottomNavigationBar is not sticking, roughly we create every time new one, but with right state.
  • German Saprykin
    German Saprykin almost 7 years
    Updated example with current index
  • CodeGrue
    CodeGrue about 6 years
    can you please elaborate on the purpose of the TickerMode widgets? This code seems to work fine also: new Offstage( offstage: _tabIndex != 1, child: new YourRightPage(), ),
  • RafaelHamasaki
    RafaelHamasaki about 6 years
    With this implementation, the back button doesn't work for accessing the previous Offstage widget. Is there any way to support this?
  • najeira
    najeira about 6 years
    @RafaelHamasaki You can handle back button using docs.flutter.io/flutter/widgets/WidgetsBindingObserver/…
  • najeira
    najeira about 6 years
    @CodeGrue I use TickerMode to stop the animation of the widgets which are not displayed.
  • RafaelHamasaki
    RafaelHamasaki about 6 years
    @najeira Yeah, that's what I ended up doing. Too bad I have to keep my own "navigation" history. Thanks anyway!
  • camino
    camino about 6 years
    @najeira it seems that the code above only support 2 BottomNavigationBarItem, if I add one more item, it give an error in the simulator: RangeError(index) invalid value:Not in range 0..1, do you know why?
  • najeira
    najeira about 6 years
    @camino You should to add both BottomNavigationBarItem and a child of Stack.
  • CodeGrue
    CodeGrue about 6 years
    You can have multiple nested MaterialApp widgets? (assuming this example is itself inside a parent MaterialApp)
  • najeira
    najeira about 6 years
    @CodeGrue Yes, you can.
  • Kirill Karmazin
    Kirill Karmazin over 4 years
    could you please elaborate more on this line: body: Navigator(key: _navigatorKey, onGenerateRoute: generateRoute), is it ok to set a Navigator widget as a body of your app? Looks strange. How does it work?
  • ThemBones
    ThemBones over 4 years
    There are one or two highly complicated tutorials on this topic, containing flaws which render them basically useless. This short snippet was the only one that works. The catch is that you need to pass the pushNamed to the navigator key. Thank you, great work.
  • Zakir
    Zakir over 4 years
    @KirillKarmazin If you want to navigate between different widgets. You have to put them under a navigator in widget tree. Since in this example we only want to change the body of the screen during navigation. So I add the navigator as the top widget in body and put other widget under it.
  • ych
    ych over 4 years
    Unfortunately, as I understand, your solution works only if the Scaffold body is the same widget but with different properties, which is not a regular case.
  • Wail Hayaly
    Wail Hayaly about 4 years
    I'm using this method with flutter_bloc it's GREAT, But when the app begins it loads the whole stacked-widgets data from the API, How can I make it load only the viewed stack widget when the user view it for the first time?
  • publicJorn
    publicJorn about 4 years
    An alternative may be IndexedStack, which provides a simpler API to Stack combined with Offstage. You may still need TickerMode though!
  • Gulnaz Ghanchi
    Gulnaz Ghanchi almost 4 years
    this helped me a lot with simple implementation. thanks a lot
  • Shakeel Ahmad
    Shakeel Ahmad almost 4 years
    Very well explained. only extra I did to use ChangeNotifier Provider for selected tab index. This way even if I route to page(bottom bar pages) from within the page, it activate the selected index.
  • Lee Probert
    Lee Probert almost 4 years
    PushReplacementNamed is Going to purge the last screen in the stack. You need all the sections in your navigation to exist simultaneously with their own navigators intact. The only way I’ve found to do this is with an IndexedStack but I don’t use a Navigator. Still looking for a good solution.
  • Amanpreet Kaur
    Amanpreet Kaur over 3 years
    This is simple and great it would be great if you can help me with handing backpressed in this
  • CopsOnRoad
    CopsOnRoad over 3 years
    @AmanpreetKaur For that you can use Set. On press of a new item, simply add its index to the set, wrap your widget in WillPopScope and inside onWillPop you can check the value of last index, if it's empty, simply exit the app else set the current index to that value.
  • Jessica
    Jessica over 3 years
    Say I'm on the home page and I want to go to the single page of the messages. How can I go to that route on a button click in the home page?
  • Nagabhushan Baddi
    Nagabhushan Baddi over 3 years
    @publicJorn Are IndexedStack and Stack+Offstage equivalent in terms of performance?
  • G H Prakash
    G H Prakash over 2 years
    nice.. it helps me a lot