28

I want to define unselected color of icon in tab just like unselectedLabelColor.

  TabBar(
          indicatorColor: Colors.grey,
          labelColor: Colors.black,
          unselectedLabelColor: Colors.grey,
          tabs: [
            Tab(
                text: 'first',
                icon: Icon(Icons.directions_car, color: Colors.grey)),
            Tab(
                text: 'second',
                icon: Icon(Icons.directions_transit, color: Colors.grey)),
            Tab(
                text: 'third',
                icon: Icon(Icons.directions_bike, color: Colors.grey)),
          ],
        )
bilal
  • 2,187
  • 3
  • 22
  • 23

13 Answers13

50

As per the directions given by Britannio, I have solved my problem but I want to share my solution so that it can help others. I am confused about one thing that I have to call setState() with empty body which is not recommended so if any one have a better solution then please comment. I'll update it.

     TabController _tabController;

     @override
     void initState() {
       super.initState();
      _tabController = new TabController(vsync: this, length: 3);
      _tabController.addListener(_handleTabSelection);
     }

     void _handleTabSelection() {
        setState(() {
         });
     }

     TabBar(
            controller: _tabController,
            indicatorColor: Colors.grey,
            labelColor: Colors.black,
            unselectedLabelColor: Colors.grey,
            tabs: [
              Tab(
                  text: 'Sale',
                  icon: Icon(Icons.directions_car,
                      color: _tabController.index == 0
                          ? Colors.black
                          : Colors.grey)),
              Tab(
                  text: 'Latest',
                  icon: Icon(Icons.directions_transit,
                      color: _tabController.index == 1
                          ? Colors.black
                          : Colors.grey)),
              Tab(
                  text: 'Popular',
                  icon: Icon(Icons.directions_bike,
                      color: _tabController.index == 2
                          ? Colors.black
                          : Colors.grey)),
            ],
          )
bilal
  • 2,187
  • 3
  • 22
  • 23
  • 4
    there is a problem with this implementation because it only works when we click on that tab but if we swipe between the tabs it doesn't work, have you seen same issue with this. – Atul Chaudhary Dec 31 '19 at 13:23
  • Also the color is changed instantly whereas the color of the Tab's text is changed gradually. So not perfect – Oleg Yablokov Nov 16 '22 at 12:18
  • remember to add `// checked for indexIsChanging to prevent rebuilding twice if (mounted && !tabController.indexIsChanging) {setState(() {});}` since tabbar listener is called twice. – Shubham Jan 22 '23 at 18:50
33

Now you can simply change color of labelColor property

bottomNavigationBar: TabBar(
    tabs: [
      
    ],
    labelColor: Colors.deepPurpleAccent,
  ),

Also remember to set unSelectedLabelColor to differ them.

Maël Pedretti
  • 718
  • 7
  • 22
Álvaro Agüero
  • 4,494
  • 1
  • 42
  • 39
13

Removing the property 'color' from the icons will use the default color set on unselectedLabelColor.

TabBar(
  indicatorColor: Colors.grey,
  labelColor: Colors.black,
  unselectedLabelColor: Colors.grey,
  tabs: [
    Tab(
        text: 'first',
        icon: Icon(Icons.directions_car)),
    Tab(
        text: 'second',
        icon: Icon(Icons.directions_transit)),
    Tab(
        text: 'third',
        icon: Icon(Icons.directions_bike)),
  ],
)
Eliel
  • 143
  • 1
  • 6
8

The code I am using when using the image icon in the tabbar.

It also works correctly with tabs and swipes.

TabBar(
      tabs: [
        Tab(
            text: 'one',
            icon: CustomIcon('assets/1.png', size: 24,)),
        Tab(
            text: 'two',
            icon: CustomIcon('assets/2.png', size: 24,)),
      ],
    )

----------------------------------------

class CustomIcon extends StatelessWidget{
  const CustomIcon(this.name, { Key key, this.size, this.color, }) : super(key: key);

  final String name;
  final double size;
  final Color color;

  @override
  Widget build(BuildContext context) {

    final IconThemeData iconTheme = IconTheme.of(context);
    final double iconOpacity = iconTheme.opacity;
    Color iconColor = color ?? iconTheme.color;

    if (iconOpacity != 1.0) iconColor = iconColor.withOpacity(iconColor.opacity * iconOpacity);
    return Image.asset(name, color: iconColor, height: size,);
  }
}
junmo choi
  • 195
  • 2
  • 7
5

My case, I use custom image from asset, this code work for me.

TabBar(
  indicatorColor: Colors.grey,
  labelColor: Colors.black,
  unselectedLabelColor: Colors.grey,
  tabs: [
    Tab(
        text: 'first',
        icon: ImageIcon(AssetImage('assets/1.png')
    ),
    Tab(
        text: 'second',
        icon: ImageIcon(AssetImage('assets/2.png')
    ),
    Tab(
        text: 'third',
        icon: ImageIcon(AssetImage('assets/3.png')
    ),
  ],
)
QHu91_IT
  • 189
  • 2
  • 12
3

There are two ways

  1. You can use activeIcon:
BottomNavigationBarItem(
        activeIcon: ,
        icon: ,
  1. You can use additional field:
IconData selectedItem = Icons.dashboard;

List<IconData> itemsList = [
  Icons.dashboard,
  Icons.location_on,
  Icons.notifications,
  Icons.account_circle,
];

//...
  BottomNavigationBar(
      onTap: (int index) {
        setState(() {
          selectedItem = itemsList[index];
        });
      },
      currentIndex: itemsList.indexOf(selectedItem),
      items: itemsList.map((data) {
        return BottomNavigationBarItem(
          icon: selectedItem == data
              ? Icon(data, color: Colors.grey)
              : Icon(data, color: Colors.grey),
          title: Container(),
        );
      }).toList());

UPD: For Tab there no activeIcon, so, it seems that you can use second way

Andrii Turkovskyi
  • 27,554
  • 16
  • 95
  • 105
  • In both ways, you are using BottomNavigationBarItem with BottomNavigationBar but I am asking for TabBar with simple Tabs. – bilal Dec 28 '18 at 12:51
  • It's just an example. As I've said - you can use second way in your case. It doesn't matter for it - `TabBar` or `BottomNavigationBar` – Andrii Turkovskyi Dec 28 '18 at 13:13
3

I think it is better to use TabBarTheme class to customize TabBar look in a single place.

ThemeData(
  tabBarTheme: TabBarTheme(
    indicatorSize: TabBarIndicatorSize.tab,
    indicatorColor: Colors.grey,
    labelColor: Colors.black,
    unselectedLabelColor: Colors.grey,
  ),
)
BambinoUA
  • 6,126
  • 5
  • 35
  • 51
2
  1. Create a custom tab controller as shown here
  2. Do something like _tabController.index to get the index of the current tab.
  3. For each tab check if its position(starting from 0) matches the TabController index and display the appropriate icon
2
   //Assign tab controller to both TabBar and TabBarView  
     TabController _tabController;
      @override
      void initState() {
        super.initState();
        _tabController = new TabController(vsync: this, length: 2);
        _tabController.addListener(_handleTabSelection);
      }
    
      void _handleTabSelection() {
        setState(() {});
      }
    
    
    
    
    Widget _tabSection(BuildContext context) {
        return DefaultTabController(
            length: 2,
            child: Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
              Container(
                height: 60,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(8),
                    border: Border.all(width: 2, color: Colors.grey[300])),
                child: TabBar(
                    controller: _tabController,
                    indicatorColor: Colors.red,
                    indicator: UnderlineTabIndicator(
                      borderSide: BorderSide(color: Colors.red, width: 2.0),
                      insets: EdgeInsets.symmetric(horizontal: 40),
                    ),
                    labelColor: Colors.red,
                    unselectedLabelColor: Colors.grey,
                    tabs: [
                      Tab(
                        icon: SvgPicture.asset(
                          'images/svgs/Car.svg',
                          fit: BoxFit.fill,
                          color: _tabController.index == 0
                              ? Color(0xffeb5757)
                              : Colors.grey,
                        ),
                        iconMargin: EdgeInsets.only(top: 5),
                        text: "Daily Car Wash",
                      ),
                      Tab(
                        icon: SvgPicture.asset(
                          'images/svgs/Bike.svg',
                          fit: BoxFit.fill,
                          color: _tabController.index == 1
                              ? Color(0xffeb5757)
                              : Colors.grey,
                        ),
                        iconMargin: EdgeInsets.only(top: 5, bottom: 2),
                        text: "Daily Bike Wash",
                      ),
                    ]),
              ),
              SizedBox(height: 15),
              Container(
                  height: MediaQuery.of(context).size.height * 0.75,
                  child: TabBarView(controller: _tabController, children: [
                    upComingsList(), //List of Upcoming Bookings
                =
                    previousList(),
                  ]))
            ]));
      }
Juned Raza
  • 228
  • 3
  • 6
  • Please don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Tyler2P Jan 14 '21 at 15:20
  • I can try to explain: if you use custom icons inside SvgPicture you can see that color of the icons doesn't change even when LabelsColor provided, so this case solve the problem. Inside color of your SvgPicture.asset use _tabController.index to check if that bar selected. Don't forget to use _tabController.addListener(_handleTabSelection) with setState or icon color doesn't change. – Richard Sipher Feb 16 '22 at 14:33
1

Here's what I did for my own project. I used the animation property created by the tab controller.

_tabController.animation!.value - index is the value of every tab icon with 0 being active and any other number indicate how far the tab icon is from the active index.

math.min(value < 0 ? (0 - value).abs() : value, 1) interpolates the above value into the range of 0-1 with 0 being active and 1 being inactive.

With these values, you can animate any property on the icon, not just the colors.

TabBar(
  controller: _tabController,
  tabs: tabs.asMap()
    .map((index, tab) =>
      MapEntry(
        index,
        Tab(
          icon: AnimatedBuilder(
            animation: _tabController.animation!,
            builder: (context, child) {
              final value = _tabController.animation!.value - index;
              return SvgPicture.asset(tab.iconAsset,
                width: 32,
                color: ColorTween(
                  begin: Color.fromRGBO(89, 194, 185, 1),
                  end: Color.fromRGBO(60, 60, 60, 1)
                ).lerp(
                  math.min(value < 0 ? (0 - value).abs() : value, 1)
                )
              );
            }
          ),
          text: tab.text,
        )
      )
    )
    .values.toList(),
)

demo

1

You can use simple variable to maintain the tab states.

    class _MainTabWidgetState extends State<MainTabWidget> {
    
      @override   void initState() {
        // TODO: implement initState
        super.initState();   
       }
    
      int selected_index = 0;
 }

Now you can change image or text as per the index.

 tabs: [
      Tab(
        child: Image.asset(
          selected_index == 0 ? 'assets/tb_home_selected.png' : 'assets/tb_home.png',
          width: 40,
          height: 40,
        ),
      ),
      Tab(
        child: Image.asset(
          selected_index == 1 ? 'assets/tb_saved_selected.png' : 'assets/tb_saved.png',
          width: 40,
          height: 40,
        ),
      ),
   ]

Now set index in Tabbar onTap

    onTap: (index) {
          setState(() {
      selected_index = index;
    });
   },
Moiz Irshad
  • 700
  • 1
  • 6
  • 15
0

I have used different approach. I have used flutter_bloc to achieve this. To understand this approach first see https://pub.dev/packages/flutter_bloc

Note: If you are using cubits then don't forget to close() them

First I created a cubit class as:

import 'package:flutter_bloc/flutter_bloc.dart';

class UICubit<T> extends Cubit<T> {
  T currentState;

  UICubit(T state) : super(state);

  void updateState(T newState) {
    this.currentState = newState;
    emit(currentState);
  }
}

After that I have created a TabIcon Class as below:

class TabIcon extends StatelessWidget {
  final Color selectedColor;
  final Color unselectedColor;
  final Icon icon;
  final double size;
  final UICubit<bool> uiCubit;

  TabIcon(this.uiCubit, this.icon, this.selectedColor, this.unselectedColor,
      this.size);

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<UICubit<bool>, bool>(
      cubit: uiCubit,
      listener: (BuildContext context,bool flag) {},
      builder: (BuildContext context, bool flag) {
        return _buildImage(flag?selectedColor:unselectedColor);
      },
    );
  }

  Widget _buildImage(int color) {
    return Image.asset(
      icon,
      width: size,
      height: size,
      color: color,
    );
  }
  
}

Now in Parent Widget I created a list of cubits as below:

List<UICubit<TabState>> cubitList = [];

Adding Tabs:

      List<Tab> tabs = [];
   cubit1 = UICubit<TabState>(true);
   cubitList.add(cubit1);
   tabs.add(Tab(
          text: "First",
          icon: TabIcon(cubit1, Icons.firsIcon, Colors.blue,Colors.grey),
        ),
    
    cubit2 = UICubit<TabState>(false);
    cubitList.add(cubit2);
    tabs.add(Tab(
          text: "Second",
          icon: TabIcon(cubit2, Icons.firsIcon, Colors.blue,Colors.grey),
        ),

Now on tab Click:

TabBar(
      onTap: (index) {
             updateTab(index);
         },
                       
           tabs: tabs,
      ),

updateTab() mehtod:

  void updateTab(int index) async {
    for (int i = 0; i < cubitList.length; i++) {
      if (i == index) {
        cubitList[i].updateState(true);
      } else {
        cubitList[i].updateState(false);
      }
    }
  }

Don't forget to close your cubits in dispose method of parent widget:

 @override
  void dispose() {
    cubitList.forEach((element) {
      element.close();
    });
    super.dispose();
  }

Thanks, May be some people don't like this approach but this approach solved my problem.

Sanjay Kumar
  • 1,135
  • 14
  • 27
0

for me selected and unselected properties of TabBar not changing the text color.

I try like below

import 'package:customer/screens/customer/payments/history.dart';
import 'package:customer/screens/customer/payments/methods.dart';
import 'package:customer/shared/services/colors.dart';
import 'package:flutter/material.dart';

class Payments extends StatefulWidget {
  @override
  _PaymentsState createState() => _PaymentsState();
}

class _PaymentsState extends State<Payments>
    with SingleTickerProviderStateMixin {
  TabController? _controller;
  int selectedIndex = 1;

  List<Widget> list = [
    Tab(text: "Methodes"),
    Tab(text: "History"),
  ];
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
      length: 2,
      child: Scaffold(
        backgroundColor: Tingsapp.white,
        appBar: AppBar(
            backgroundColor: Tingsapp.white,
            elevation: 0,
            title: Text("Payments"),
            leading: IconButton(
              icon: Icon(Icons.arrow_back_ios_rounded),
              onPressed: () => Navigator.pop(context),
            ),
            bottom: CustomTabBar()),
        body: TabBarView(
          children: [
            Padding(padding: const EdgeInsets.all(8.0), child: Methods()),
            Padding(padding: const EdgeInsets.all(8.0), child: History()),
          ],
        ),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _controller =
        TabController(length: list.length, vsync: this, initialIndex: 1);
    _controller?.addListener(() {
      selectedIndex = _controller!.index;
    });
  }

  @override
  void dispose() {
    super.dispose();
    _controller?.dispose();
  }
}

class CustomTabBar extends StatelessWidget implements PreferredSizeWidget {
  @override
  Size get preferredSize => Size.fromHeight(kToolbarHeight);

  @override
  Widget build(BuildContext context) {
    final TabController? tabController = DefaultTabController.of(context);
    final Color selectedColor = Colors.blue;
    final Color unselectedColor = Colors.red;

    return TabBar(
      indicatorColor: Tingsapp.transparent,
      tabs: [
        Tab(
          child: Text(
            'Methods',
            style: TextStyle(
              color:
                  tabController?.index == 0 ? selectedColor : unselectedColor,
            ),
          ),
        ),
        Tab(
          child: Text(
            'History',
            style: TextStyle(
              color:
                  tabController?.index == 1 ? selectedColor : unselectedColor,
            ),
          ),
        ),
      ],
      controller: tabController,
    );
  }
}

Zia
  • 506
  • 3
  • 20