Search code examples
androidflutteruser-interfacelayoutwidget

How can I create a custom swipable tab bar like this in Flutter?


enter image description here

I want to create a tab bar like the one above which can be swipable with animations. I tried doing this with the default Tab bar widget in Flutter but failed miserably. How can I make this tab bar swipable with animations?

What is the logic behind bringing the selected tab animation down from "Friends" tab to "Family" tab?

I figured out the onHorizontalDrag property from the GestureDetector widget to do the swiping but still can't wrap my head around switching the tabs with animation.

It'd be so great if someone could help me with this.


Solution

  • This is made using PageView and customAppBar. However, if you want clickEvent on tabBaritem, wrap with GestureDetector make a callBack or stateManagement to hold index.

    Result

    enter image description here

    MainWidget

    
    class BU extends StatefulWidget {
      BU({Key? key}) : super(key: key);
    
      @override
      _BUState createState() => _BUState();
    }
    
    class _BUState extends State<BU> {
      int _selectedIndex = 0;
    
      final PageController controller = PageController(initialPage: 0);
    
      @override
      void initState() {
        super.initState();
    
        widgets = [
          ...List.generate(
            5,
            (index) => Container(
              alignment: Alignment.center,
              color: index.isEven ? Colors.cyanAccent : Colors.deepPurple,
              child: Text(
                "Item $index",
                style: TextStyle(fontSize: 44),
              ),
            ),
          )
        ];
    
        controller.addListener(() {
          if (controller.hasClients) {
            setState(() {
              /// you can ceil foor
              _selectedIndex = controller.page!.toInt();
            });
          }
        });
      }
    
      @override
      void dispose() {
        controller.dispose();
        super.dispose();
      }
    
      late List<Widget> widgets;
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Column(
            children: [
              AppBar(
                selectedItem: _selectedIndex,
              ),
              Expanded(
                child: Container(
                    width: double.infinity,
                    child: PageView(
                      controller: controller,
                      children: [...widgets],
                    )),
              )
            ],
          ),
        );
      }
    }
    

    TabBar

    
    class AppBar extends StatelessWidget {
      final int selectedItem;
    
      const AppBar({
        Key? key,
        required this.selectedItem,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Container(
          padding: EdgeInsets.all(8),
          color: Colors.white,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  TabItemChip(
                    isSelected: selectedItem == 0,
                    text: "Profile",
                  ),
                  TabItemChip(
                    isSelected: selectedItem == 1,
                    text: "About",
                  ),
                  TabItemChip(
                    isSelected: selectedItem == 2,
                    text: "Friends",
                  ),
                ],
              ),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  TabItemChip(
                    isSelected: selectedItem == 3,
                    text: "Family",
                  ),
                  TabItemChip(
                    isSelected: selectedItem == 4,
                    text: "Settings",
                  ),
                ],
              )
            ],
          ),
        );
      }
    }
    
    

    TabBarItemChip

    class TabItemChip extends StatelessWidget {
      final bool isSelected;
      final String text;
      const TabItemChip({
        Key? key,
        required this.text,
        required this.isSelected,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return AnimatedContainer(
          duration: Duration(milliseconds: 400),
          height: 40,
          width: 140,
          alignment: Alignment.center,
          decoration: BoxDecoration(
            color: isSelected ? Colors.cyanAccent.shade700 : Colors.white,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Text(
            text,
            style: TextStyle(
              color: isSelected ? Colors.white : Colors.black,
              fontWeight: FontWeight.bold,
            ),
          ),
        );
      }
    }