Reputation: 1499
I'm changing the color of a custom menu icon, by using the setState
in my Custom Icon Widget
Although I'd like to change the previous pressed Icon to the previous state (before the color was Indigo
)
Live demo: https://alexakis97.github.io/webappjs/
By changing the _color
variable in onTap method I manage to change the color.
To get a better understanding this an Custom Icon Widget:
class _CustomBottomIconState extends State<CustomBottomIcon> {
final Function changeMenu;
final int index;
_CustomBottomIconState({this.changeMenu, this.index});
Color _color = Colors.indigo;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
//
changeMenu(index);
setState(() {
_color = Colors.green;
});
},
child: Container(
decoration: BoxDecoration(
color: _color,
borderRadius: BorderRadius.circular(100),
),
height: MediaQuery.of(context).size.height / 20,
width: MediaQuery.of(context).size.height / 20,
child: Icon(
widget.icon,
color: Colors.white,
),
),
);
}
}
(Middle Icon pressed)
How can I change the first one back to Indigo
?
Sharing bottom menu widget
class BottomMenu extends StatelessWidget {
final Function changeMenu;
final Key key;
BottomMenu({this.changeMenu, this.key});
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height / 15,
width: MediaQuery.of(context).size.width / 1.5,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
)),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomBottomIcon(
icon: Icons.wallet_travel,
index: 0,
changeMenu: changeMenu),
SizedBox(
width: 10,
),
CustomBottomIcon(
icon: Icons.person, index: 1, changeMenu: changeMenu),
SizedBox(
width: 10,
),
CustomBottomIcon(
icon: Icons.email, index: 2, changeMenu: changeMenu),
SizedBox(
width: 10,
),
],
),
)),
);
}
}
and this is what the ChangeMenu
function does:
final List<Widget> list = [
Text("Work 1"),
Text("Work 2"),
Text("Work 3"),
];
int i = 0;
void changeMenu(index) {
setState(() {
i = index;
});
}
It's placed on another parent widget
Upvotes: 1
Views: 1208
Reputation: 1876
This is a very similar idea to a radio button, which includes two parameters, value
and groupValue
. You can implement yours in a similar way: change your BottomMenu
to a StatefulWidget
, add a int groupValue = -1
in your state, and pass this to your CustomIconButton
, as well as index
.
You can the choose the colour of your button based on whether index == groupIndex
inside your CustomIconButton
and update groupIndex
in your BottomMenu
class every time a button is pressed. You can do this by wrapping your changeMenu
parameter with something like
changeMenu: (index) {
setState(() {
groupIndex = index;
});
changeMenu(index);
},
or move this to a separate function.
If you want to turn all of them off when you press one of them a second time, just add this a check to the wrapper function and set groupIndex
to -1 if it's the same as index
.
Upvotes: 0
Reputation: 1143
In a case like yours it is best to use a state management library such as Provider or BloC. But if you want to stick to your approach, then you will have to modify your code to be the following:
Custom Icon Widget
class _CustomBottomIconState extends State<CustomBottomIcon> {
final Function changeMenu;
final int index;
final int selectedIndex;
_CustomBottomIconState({this.changeMenu, this.index, this.selectedIndex});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => changeMenu(index),
child: Container(
decoration: BoxDecoration(
color: selectedIndex == index ? Colors.green : Colors.indigo,
borderRadius: BorderRadius.circular(100),
),
height: MediaQuery.of(context).size.height / 20,
width: MediaQuery.of(context).size.height / 20,
child: Icon(
widget.icon,
color: Colors.white,
),
),
);
}
}
Bottom Menu
class BottomMenu extends StatelessWidget {
final Function changeMenu;
final Key key;
final int selectedIndex;
BottomMenu({this.changeMenu, this.key, this.selectedIndex});
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.bottomCenter,
child: Container(
height: MediaQuery.of(context).size.height / 15,
width: MediaQuery.of(context).size.width / 1.5,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(20.0),
topRight: const Radius.circular(20.0),
),
),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomBottomIcon(
index: 0,
icon: Icons.wallet_travel,
selectedIndex: selectedIndex,
changeMenu: changeMenu,
),
SizedBox(width: 10),
CustomBottomIcon(
index: 1,
icon: Icons.person,
selectedIndex: selectedIndex,
changeMenu: changeMenu,
),
SizedBox(width: 10),
CustomBottomIcon(
index: 2,
icon: Icons.email,
selectedIndex: selectedIndex,
changeMenu: changeMenu,
),
SizedBox(width: 10),
],
),
),
),
);
}
}
Change Menu Function
void changeMenu(int _index) {
setState(() {
selectedIndex = _index;
});
}
Now for the explanation, what I am doing is that I am doing a technique called state lifting, this technique is highly discouraged since it can get very messy very fast and hard to properly maintain. But for a small portion of the app it is possible to get away with it. 😉
Now what is happening is that we have a main or any class as the parent and is the one managing the state.
Below is the tree representation of your widget structure.
Now when the icon is clicked, the method in the main (changeMenu
) is called which takes the index of the icon in the BottomBar
and set the selectedIndex
to be it. This will cause a rebuild in Flutter since a state was changed. Therefore the BottomBar
will be rebuilt, and it will pass the selectedIndex
to the icons. Inside the icons, there exist a check which checks if the selectedIndex
is equal to the icon index
, depending on this check, the color is set.
Note: The initial value of selectedIndex
, will determine which icon is selected, set it to -1 to make no icon selected.
Upvotes: 1
Reputation: 215
Try this one,
class _CustomBottomIconState extends State<CustomBottomIcon> {
final Function changeMenu;
final int index;
int _selectedIndex = -1; // by default all button color will be green, if _selectedIndex = 0 the first index color will be indigo
_CustomBottomIconState({this.changeMenu, this.index});
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
//
changeMenu(index);
setState(() {
_selectedIndex = index;
});
},
child: Container(
decoration: BoxDecoration(
color: getColor(index),
borderRadius: BorderRadius.circular(100),
),
height: MediaQuery.of(context).size.height / 20,
width: MediaQuery.of(context).size.height / 20,
child: Icon(
widget.icon,
color: Colors.white,
),
),
);
}
Color getColor(int index) {
if (index == _selectedIndex) {
return Colors.indigo;
} else {
return Colors.green;
}
}
}
Upvotes: 0