online software programming courses

Flutter Tabbar With And Without AppBar

We will learn how to build good looking TabBar with and without AppBar

Download the starter code from the link below

Flutter Tab Bar Without App Bar

This is what we are going to build. Let's take a look at the picture. You can find the complete app tut here

From the above photo, we can see that we will three tabbars. We don't have any app bar in this example. 

We will have to know the idea about two things. 

TabBar

TabBarView

Controller for TabBar and TabBarView

TabBar

It takes list of children. The property is tabs. For tabs we can use tab widget or any other wideget within it.

TabBar(
  ............
  ............
  tabs:[
  Tab(.....),
  ............
  ]
)

 

TabBarView

It takes children just like TabBar. These are children would be shown when you tap on the TabBar tabs.

TabBarView(
  ............
  ............
children:[
   ................
   ............
  ]
)

 

 

For doing this we will simply just create two Containers (Container() widget). You may not put them in the Container() widget. You can style them anyway you want. One Container() for holding the tabbars and another Container() for holding view for the TabBar which is TabBarView.

Look at the TabBar in the first Container()

Container(
            ......................................
            ......................................
            child: Align(
              alignment: Alignment.centerLeft,
              child: TabBar(
             .....................
             .....................
                tabs: [
                  Tab(text: 'Places'),
                  Tab(text: 'Inspiration'),
                  Tab(text: 'Emotions'),
                ],
              ),
            ),
    ),

In the Container() we have TabBar and TabBar takes tabs list. You can put as many children in the list. Children could be Tab or any kind of widget.

Look at the second Container() for TabBarView. 

Container(
            padding: const EdgeInsets.only(left: 20),
            height: 300,
            width: double.maxFinite,
            child: TabBarView(
              controller: _tabController,
              children: [
                ListView.builder(
                    scrollDirection: Axis.horizontal,
                    itemCount: 5,
                    itemBuilder: (_, i) {
                      return GestureDetector(
                          onTap: () {},
                          child: Container(
                            width: 200,
                            height: 300,
                            margin: const EdgeInsets.only(
                                right: 10, top: 10, bottom: 0),
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(20),
                              image: DecorationImage(
                                  image: AssetImage("img/mountain1.jpeg"),
                                  fit: BoxFit.fitHeight),
                            ),
                          ));
                    }),
                ListView.builder(
                    scrollDirection: Axis.vertical,
                    itemCount: 5,
                    itemBuilder: (_, i) {
                      return GestureDetector(
                          onTap: () {},
                          child: AnimatedOpacity(
                              opacity: animation.value,
                              duration: Duration(milliseconds: 2000),
                              child: Container(
                                width: 200,
                                height: 300,
                                margin: const EdgeInsets.only(
                                    right: 10, top: 10, bottom: 0),
                                decoration: BoxDecoration(
                                  borderRadius: BorderRadius.circular(20),
                                  image: DecorationImage(
                                      image: AssetImage("img/mountain1.jpeg"),
                                      fit: BoxFit.fitHeight),
                                ),
                              )));
                    }),
                Material(
                  child: ListTile(
                    leading: CircleAvatar(
                      backgroundColor: Colors.grey,
                    ),
                    title: Text("Content"),
                  ),
                ),
              ],
            ),
          )

Here the TabBarView also takes list of children.

 

Controller

At the same time, it also needs a controller. We need to create a controller for TabBar.  You can create it within your initState() class or somewhere it's easy to initialize.

TabController _tabController = TabController(length: 3, vsync: this);

 

Indicator

For indicator property if you want to use custome desgin you need to implement it using Decoration widget. It means your custom widget should extend Decoration

And if you extend Decoration, you should implement and override createBoxPainter method. And then createBoxPainter should return a widget. 

And this widget should extend BoxPainter. and at the same time this returned widget must implement and override paint method. See the skeleton

class CircleTabIndicator extends Decoration {
 final Color color;
  double radius;

  CircleTabIndicator({required Color color, required double radius})
  //override createBoxPainter

  @override
  BoxPainter createBoxPainter([VoidCallback? onChanged]) {
    return _CircelPainter();
  }
}

class _CirclePainter extends BoxPainter {
 ...............................
 ...............................

//override paint 

  @override
  void paint(Canvas canvas, Offset offset, ImageConfiguration cfg) {
  ......
  }
}

 

Vertical TabBar

Three things we need to do.

Wrap your TabBar inside RotatedBox and set quarerTurns:1

Put those two Container() (TabBar and TabBarView) inside Flexible

Put Flexible inside Row() widget

If you want your TabBar to be Vertical you must put the TabBar inside RotatedBox() widget as a child.

If you want your TabBar to be vertical, then you need to put those Container()'s(TabBar and TabBarView) insde Flexible widget, this is because we need to take all the available space. And we also need to mention the contraints, the limits of your widget. Here by limit I mean width.

And then wrap your Flexible widget in side Row() widget. Row takes unlimited bound or space. Now Flexible will take unlimited bound too or unlimited width. 

Flexible widget always needs to constrains(width or height) of it's parents, whether it's limited or unlimited.

Vertical TabBar Complete Code

 Row(
                  children: [
                    RotatedBox(
                      quarterTurns: 1,
                      child: Container(
                        child: Align(
                          alignment: Alignment.centerLeft,
                          child: TabBar(
                              labelPadding: const EdgeInsets.only(left: 20, right: 20),
                              controller: _tabController,
                              labelColor: Colors.black,
                              unselectedLabelColor: Colors.grey,
                              isScrollable: true,
                              indicatorSize: TabBarIndicatorSize.label,
                              indicator:
                              CircleTabIndicator(color: AppColors.mainColor, radius: 4),
                              tabs: [
                                Tab(text: "Places"),
                                Tab(text: "Inspiration"),
                                Tab(text: "Emotions"),
                              ]),
                        ),
                      ),
                    ),
                    Flexible(child: Container(
                      padding: const EdgeInsets.only(left: 20),
                      height: 300,
                      width: double.maxFinite,
                      child: TabBarView(
                        controller: _tabController,
                        children: [
                          ListView.builder(
                            itemCount: info.length,
                            scrollDirection: Axis.horizontal,
                            itemBuilder: (BuildContext context, int index) {
                              return GestureDetector(
                                onTap:(){
                                  BlocProvider.of<AppCubits>(context).detailPage(info[index]);
                                },
                                child: Container(
                                  margin: const EdgeInsets.only(right: 15, top: 10),
                                  width: 200,
                                  height: 300,
                                  decoration: BoxDecoration(
                                      borderRadius: BorderRadius.circular(20),
                                      color: Colors.white,
                                      image: DecorationImage(
                                          image: NetworkImage("http://mark.bslmeiyu.com/uploads/"+info[index].img),
                                          fit: BoxFit.cover)),
                                ),
                              );
                            },
                          ),
                          ListView.builder(
                              itemBuilder: (context, index){
                                return ListTile(
                                  leading: Icon(Icons.account_circle),
                                  title: Text("Line "+(index+1).toString()),
                                  selectedTileColor:Colors.green[400],
                                  onTap: () {
                                    setState(() {

                                    });
                                  },
                                );
                              }),
                          Text("Bye")
                        ],
                      ),
                    ),)
                  ],
                ),

Here if you don't use Flexible widget correctly, you will get the following error

The overflowing RenderFlex has an orientation of Axis.horizontal.