Paul
Paul

Reputation: 2103

How to use BottomNavigationBar with Navigator?

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?

Upvotes: 49

Views: 70981

Answers (8)

Ahmed tharwat
Ahmed tharwat

Reputation: 15

code example here :

last updated code

enter image description here

int _selectedIndex = 0;
@override
Widget build(BuildContext context) {
 return Scaffold(
  body: Stack(
    children: [
      Offstage(
        offstage: _selectedIndex != 0,
        child: TickerMode(
          enabled: _selectedIndex == 0,
          child: MaterialApp(
              debugShowCheckedModeBanner: false, home: 
  NavigationBarTest()),
        ),
      ),
       Offstage(
        offstage: _selectedIndex != 1,
        child: TickerMode(
          enabled: _selectedIndex == 1,
          child: MaterialApp(
              debugShowCheckedModeBanner: false,
              home: NavigationBarTest2()),
        ),
      ),
      Offstage(
        offstage: _selectedIndex != 2,
        child: TickerMode(
          enabled: _selectedIndex == 2,
          child: MaterialApp(
              debugShowCheckedModeBanner: false,
              home: NavigationBarTest3()),
        ),
      ),
    ],
    ),

bottomNavigation_bar code ex:

    bottomNavigationBar: NavigationBar(
     destinations: const [
      NavigationDestination(icon: Icon(Icons.home), label: 'home'),
      NavigationDestination(icon: Icon(Icons.settings), label: 'setting'),
      NavigationDestination(icon: Icon(Icons.person), label: 'profile')
    ],
    selectedIndex: _selectedIndex,
    onDestinationSelected: (int index) => setState(() {
      _selectedIndex = index;
    }),
    animationDuration: const Duration(seconds: 1),
  ),

NavigationBarTest

  class NavigationBarTest extends StatelessWidget {
    Widget build(BuildContext context) {
      return Container(
       color: Colors.amber,
    );
   }
 }

  class NavigationBarTest2 extends StatelessWidget {
    Widget build(BuildContext context) {
     return Container(
       color: Colors.red,
      );
    }
 }

  class NavigationBarTest3 extends StatelessWidget {
    Widget build(BuildContext context) {
     return Container(
      color: Colors.teal,
      );
     }
   }

Upvotes: 0

Mahesh Jamdade
Mahesh Jamdade

Reputation: 20369

Glad You asked, I experimented with this a couple of months back and tried to simplify this through a blog post. I won't be able to post the complete code here since it is pretty long, But I can certainly link all the resources to clarify it.

  1. Everything about the BottomNavigationBar in flutter
  2. complete sample code
  3. Dartpad demo
  4. If you prefer you can also depend on this package https://pub.dev/packages/navbar_router

Here's the resulting output of what the article helps you build

enter image description here

Upvotes: 2

Mukta
Mukta

Reputation: 1567

This is the code I am using in my project. If you try to avoid page viewer so you can try this

import 'package:flutter/material.dart';

class Dashboard extends StatefulWidget {
  const Dashboard({Key? key}) : super(key: key);

  @override
  State<Dashboard> createState() => _DashboardState();
}

class _DashboardState extends State<Dashboard> {
  int _selectedIndex = 0;

  void _onItemTapped(int index) {
    setState(() {
      _selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Sample'),
      ),
      body: SingleChildScrollView(
       child: Column(
        children: [
          if (_selectedIndex == 0)
            // you can call custom widget here
            Column(
              children: const [
                Text("0"),
              ],
            )
          else if (_selectedIndex == 1)
             Column(
                children: const [
                  Text("1"),
                ],                 
            )
          else             
               Column(
                children: const [
                  Text("2"),
                ],               
            ),
        ],
      ),
     ),

      bottomNavigationBar: BottomNavigationBar(
        items: const <BottomNavigationBarItem>[
          BottomNavigationBarItem(
            icon: Icon(Icons.headphones),
            label: 'Home',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.business),
            label: 'Business',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.school),
            label: 'School',
          ),              
        ],
        currentIndex: _selectedIndex,
        selectedItemColor: Colors.amber[800],
        unselectedItemColor: Colors.grey,
        onTap: _onItemTapped,
      ),
    );
  }
}

Happy Coding

Upvotes: -1

Zakir
Zakir

Reputation: 1565

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

Upvotes: 16

CopsOnRoad
CopsOnRoad

Reputation: 268384

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")),
      ],
    ),
  );
}

Upvotes: 22

Kit Mateyawa
Kit Mateyawa

Reputation: 197

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

I had to use true to enable back button.

NB: I was using Navigator.pushNamed() for navigation.

Upvotes: 0

najeira
najeira

Reputation: 3253

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.

Upvotes: 60

German Saprykin
German Saprykin

Reputation: 6961

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:

Upvotes: 3

Related Questions