Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need a persistent/same Bottom Navigation Bar for all screens - Flutter

Tags:

flutter

dart

I am a beginner with flutter and dart. I have been trying to implement a navigationBar on three different pages in my app. The toggling works well for an individual page but I have problems persisting the active and inactive tabs state on all the pages. It seems like when it navigates to another page, I lose the active state too the tabs. This is my code.

AppFooter.dart

import 'package:flutter/material.dart';

class AppFooter extends StatefulWidget {
  @override
  _AppFooterState createState() => _AppFooterState();
}

class _AppFooterState extends State<AppFooter> {
  int index = 0;
  @override
  Widget build(BuildContext context) {
    return new Theme(
      data: Theme.of(context).copyWith(
          // sets the background color of the `BottomNavigationBar`
          canvasColor: Colors.white,
          // sets the active color of the `BottomNavigationBar` if `Brightness` is light
          primaryColor: Colors.green,
          textTheme: Theme.of(context)
              .textTheme
              .copyWith(caption: new TextStyle(color: Colors.grey))),
      child: new BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          currentIndex: index,
          onTap: (int index) {
            setState(() {
              this.index = index;
            });
          switch (index){
            case 0:  Navigator.of(context).pushNamed('/dashboard');
            break;
            case 1:  Navigator.of(context).pushNamed('/medical centre');
            break;
            case 2:  Navigator.of(context).pushNamed('/history');
            break;

          }

          },
          items: [
            new BottomNavigationBarItem(
                backgroundColor: Colors.white,
                icon: index==0?new Image.asset('assets/images/dashboard_active.png'):new Image.asset('assets/images/dashboard_inactive.png'),
                title: new Text('Dashboard', style: new TextStyle(fontSize: 12.0))),
           new BottomNavigationBarItem(
               backgroundColor: Colors.white,
               icon: index==1?new Image.asset('assets/images/medical_sevice_active.png'):new Image.asset('assets/images/medical_sevice_inactive.png'),
               title: new Text('Health Services', style: new TextStyle(fontSize: 12.0))),
            new BottomNavigationBarItem(
                icon: InkWell(
                  child: Icon(
                    Icons.format_align_left,
                   // color: green,
                    size: 20.0,
                  ),
                ),
                title: new Text('History', style: new TextStyle(fontSize: 12.0))),
          ]),
    );
  }
}
like image 476
bennycodest Avatar asked Feb 19 '19 13:02

bennycodest


5 Answers

If I understand your question correctly, you need the bottom navigation bar persisted on all three pages. There is a well-written article on how to achieve it. You can find the details here.

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

https://github.com/bizz84/nested-navigation-demo-flutter

All credits go to the original author.

like image 158
Abin Avatar answered Oct 20 '22 14:10

Abin


Another great solution is the persistent_bottom_nav_bar package provided by Bilal Shahid.

It is easy to use and offers you a bunch of features:

  • Highly customizable persistent bottom navigation bar.
  • Ability to push new screens with or without bottom navigation bar.
  • 20 styles for the bottom navigation bar.
  • Includes functions for pushing screen with or without the bottom navigation bar i.e. pushNewScreen() and pushNewScreenWithRouteSettings().
  • Based on flutter's Cupertino(iOS) bottom navigation bar.
  • Can be translucent for a particular tab.
  • Custom styling for the navigation bar. Click here for more information. Handles hardware/software Android back button.

Before I found this package I followed the solution from the article @Abin mentioned in his answer. But I ran into the problem, that all screens from the navbar beeing loaded on first load of the navbar which is not that perfomant. I did not mangaed to solve this, but luckily Bilal Shahid provide a good solution with his package.

All credits to him.

like image 11
Matthias Avatar answered Oct 20 '22 14:10

Matthias


Use PageView and bottomNavigationBar:

import 'package:flutter/material.dart';

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

/// This Widget is the main application widget.
class MyApp extends StatelessWidget {
  static const String _title = 'Flutter App';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: _title,
      home: App(),
    );
  }
}

class App extends StatefulWidget {
  App({Key key}) : super(key: key);
  _AppState createState() => _AppState();
}

class _AppState extends State<App> {
  PageController _myPage;
  var selectedPage;

  @override
  void initState() {
    super.initState();
    _myPage = PageController(initialPage: 1);
    selectedPage = 1;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: PageView(
          physics: NeverScrollableScrollPhysics(),
          controller: _myPage,
          children: <Widget>[
            Center(
              child: Text("Another Page"),
            ),
            Center(
                child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text("Page 1"),
                RaisedButton(
                  onPressed: () {
                    _myPage.jumpToPage(0);
                    setState(() {
                      selectedPage = 0;
                    });
                  },
                  child: Text("Go to another page"),
                )
              ],
            )),
            Center(child: Text("Page 2")),
            Center(child: Text("Page 3")),
          ],
        ),
        bottomNavigationBar: BottomAppBar(
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: <Widget>[
              IconButton(
                icon: Icon(Icons.home),
                color: selectedPage == 1 ? Colors.blue : Colors.grey,
                onPressed: () {
                  _myPage.jumpToPage(1);
                  setState(() {
                    selectedPage = 1;
                  });
                },
              ),
              IconButton(
                icon: Icon(Icons.star),
                color: selectedPage == 2 ? Colors.blue : Colors.grey,
                onPressed: () {
                  _myPage.jumpToPage(2);
                  setState(() {
                    selectedPage = 2;
                  });
                },
              ),
              IconButton(
                icon: Icon(
                  Icons.settings,
                ),
                color: selectedPage == 3 ? Colors.blue : Colors.grey,
                onPressed: () {
                  _myPage.jumpToPage(3);
                  setState(() {
                    selectedPage = 3;
                  });
                },
              ),
            ],
          ),
        ));
  }
}

In addition, if you want preserve the state between pages such that going to another page won't cause the previous page to lose its state, use AutomaticKeepAliveClientMixin

Also, to lazily load the pages, PageView.builder is another solution.

Hope it helps.

like image 12
Mais Alheraki Avatar answered Oct 20 '22 14:10

Mais Alheraki


Just copy & past :)

main.dart:

void main() async{
  runApp(MyGrillApp());
}

class MyGrillApp extends StatelessWidget {
  const MyGrillApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(

      routes: {
        '/mainlayout': (context) => MainLayout(),
        '/page1': (context) => Page1(),
        '/page2': (context) => Page2(),
        '/page3': (context) => Page3(),
        '/page4': (context) => Page4(),
      },
      initialRoute: '/mainlayout',
    );
  }
}

main_layout.dart:

class MainLayout extends StatefulWidget {
  @override
  _MainLayoutState createState() => _MainLayoutState();
}

class _MainLayoutState extends State<MainLayout> {
  int _currentIndex = 0;

  final _page1 = GlobalKey<NavigatorState>();
  final _page2 = GlobalKey<NavigatorState>();
  final _page3 = GlobalKey<NavigatorState>();
  final _page4 = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButtonLocation: FloatingActionButtonLocation.miniCenterDocked,
      floatingActionButton: Padding(
        padding: const EdgeInsets.all(6.0),
        child: FloatingActionButton(
          backgroundColor: Colors.redAccent,
          child: const Icon(Icons.add, color: Colors.white),
          onPressed: () {
            // ToDo...
          },
        ),
      ),
      body: IndexedStack(
        index: _currentIndex,
        children: <Widget>[
          Navigator(
            key: _page1,
            onGenerateRoute: (route) => MaterialPageRoute(
              settings: route,
              builder: (context) => Page1(),
            ),
          ),
          Navigator(
            key: _page2,
            onGenerateRoute: (route) => MaterialPageRoute(
              settings: route,
              builder: (context) => Page2(),
            ),
          ),
          Navigator(
            key: _page3,
            onGenerateRoute: (route) => MaterialPageRoute(
              settings: route,
              builder: (context) => Page3(),
            ),
          ),
          Navigator(
            key: _page4,
            onGenerateRoute: (route) => MaterialPageRoute(
              settings: route,
              builder: (context) => Page4(),
            ),
          ),
        ],
      ),
      bottomNavigationBar: BottomAppBar(
        shape: const CircularNotchedRectangle(),
        clipBehavior: Clip.antiAlias,
        child: BottomNavigationBar(
          backgroundColor: Colors.white,
          currentIndex: _currentIndex,
          onTap: (index) {
            setState(() {
              _currentIndex = index;
            });
          },
          type: BottomNavigationBarType.fixed,
          selectedItemColor: Colors.redAccent,
          unselectedItemColor: Colors.grey,
          showSelectedLabels: false,
          showUnselectedLabels: false,
          items: const <BottomNavigationBarItem>[
            BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
            BottomNavigationBarItem(icon: Icon(Icons.date_range), label: 'Statistics'),
            BottomNavigationBarItem(icon: Icon(Icons.wallet_giftcard), label: 'Wallet'),
            BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
          ],
        ),
      ),
    );
  }
}

Details screen:

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

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

class _ItemDetailsPageState extends State<ItemDetailsPage> with AutomaticKeepAliveClientMixin{
  @override
  Widget build(BuildContext context) {
    super.build(context);
    return Scaffold(
        appBar: AppBar(
          backgroundColor: themeColorPrimary,
          title: Text('Item details',),
        ),
        body : Container(child: Text('Hello from details'),));
  }

  @override
  bool get wantKeepAlive => true;
}

A note about routing in my solution:

If you encounter trouble when you routing by:

Navigator.pushNamed(context, '/page3'); 

or by:

Navigator.of(context).pushNamed(Page3());

You can fix it using MaterialPageRoute:

Navigator.pushReplacement(
  context,
  MaterialPageRoute(
    builder: (context) => Page3(),
  ),
);
like image 6
Husam Ebish Avatar answered Oct 20 '22 13:10

Husam Ebish


You can use IndexedStack to persist State when you touch/change the page

Scaffold(
  body: SafeArea(
    top: false,
    child: IndexedStack(
      //Permet de garder le state des vues même quand on change de vue
      index: _currentIndex,
      children: _children,
    ),
  ),
  bottomNavigationBar: BottomNavigationBar( items: [ ] ),
);
like image 2
YAO ALEX DIDIER AKOUA Avatar answered Oct 20 '22 13:10

YAO ALEX DIDIER AKOUA