Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TabBarView or IndexedStack for BottomNavigationBar - Flutter

What is the best way to do the bottom navigation bar in flutter?

According to Getting to the Bottom of Navigation in Flutter the flutter team used IndexedStack with Offstage to show the widget when the user changes the tab, but I see there's another way of doing this by TabBarView to change between widgets with simple slide animation and also keeping each widget's scroll state

So what's the difference between the IndexedStack + Offstage and TabBarView? and what is the best way to change the current tab should I use something like flutter_bloc or just use setState()?

like image 531
Wail Hayaly Avatar asked Apr 24 '20 18:04

Wail Hayaly


1 Answers

OverView

Well, there are many ways to implement BottomNavigationBar in Flutter. But Using the IndexedStack method would create all the screens of theBottomNavigationBar at the starting. This can be fixed using TabBarView.

Here's how I Implemented BottomNavigationBar in my app using CupertinoTabBar and PageView as it would make only one screen at the starting. And also using AutomaticKeepAliveMixin as it wouldn't let the screens to be recreated again.


KeyPoints

  • PageView with PageController by which you can easily shift between screens.
  • AutomaticKeepAliveClientMixin doesn't let the screens to be recreated and thus there is no need to use IndexedStack.
  • Use Provider and Consumer to recreate only the CupertinoTabBar when changing the currentIndex. Instead of using setState(), as it would recreate the whole screen letting all the widgets to get rebuild. But here were are using Provider to recreate only TabBar.

Code Example

HomePage (BottomNavigtionBar)

class HomeScreen extends StatefulWidget {
  @override
 _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
PageController _pageController;

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

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

@override
Widget build(BuildContext context) {
return  Scaffold(
    // Wrapping the CupertinoTabBar in Consumer so that It only get 
    // recreated.
    bottomNavigationBar: Consumer<HomeVM>(
      builder: (context, model, child) {
        return CupertinoTabBar(
            backgroundColor: Colors.white10,
            currentIndex:  model.currentPage,
            onTap: (index) {
                index == model.currentPage
                ? print('same screen')
                : _pageController.jumpToPage(
                    index,
                  );
                 model.changePage(index);
                }, 
            items: bottomNavItems);
      },
    ),
    body:ChangeNotifierProvider(
           create: (_) => locator<HelpVM>(),
           child: SafeArea(
                    top: false,
                    child: PageView(
                    controller: _pageController,
                    physics: NeverScrollableScrollPhysics(),
                    children: <Widget>[
                          FrontScreen(),
                          WorkRootScreen(),
                          HelpScreen(),
                          AccountScreen(),
                     ],
                   ),
          ),
       ),
  );
 }

  const List<BottomNavigationBarItem> bottomNavItems = 
   <BottomNavigationBarItem>[
     BottomNavigationBarItem(
       icon: const Icon(
       FontAwesomeIcons.home,
     ),
   ),
    //...... bottomNavigationBarItems 
  ];

}

HomeVM (Using Provider to change the index and recreate only TabBar using Consumer)

class HomeVM extends ChangeNotifier {
 int _currentPage = 0;

 int get currentPage => _currentPage;

 void changePage(int index) {
   this._currentPage = index;
   notifyListeners();
 }
}

FrontScreen (Here we are using AutomaticKeepAliveClientMixin to retain the state by not recreating the Widget)

   class FrontScreen extends StatefulWidget {
    @override
      _FrontScreenState createState() => _FrontScreenState();
    }
    
    class _FrontScreenState extends State<FrontScreen>
        with AutomaticKeepAliveClientMixin {
      @override
      Widget build(BuildContext context) {
      // VIMP to Add this Line.
        super.build(context);
        return SafeArea(
         // Your Screen Code   
        );
      }
    
      @override
      bool get wantKeepAlive => true;
   }
like image 186
DIVYANSHU SAHU Avatar answered Nov 09 '22 00:11

DIVYANSHU SAHU