How to change color of tab's icon in selected and unselected state in flutter?

Viewed 42551

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)),
          ],
        )
10 Answers

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)),
            ],
          )

Now you can simply change color of labelColor property

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

Also remember to set unSelectedLabelColor to differ them.

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)),
  ],
)

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,);
  }
}

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

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')
    ),
  ],
)

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,
  ),
)
  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
   //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(),
                  ]))
            ]));
      }

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.

Related