I would like to use setState
to change the colour of a button to blue when it is clicked, and to change the other buttons to grey to show they are unselected. I would also like it to change the content of the page below the buttons.
Here is what it looks like currently. By default, the Table view button is selected, and the Heatmap diagram is unselected. In this case, the "Table view" text below the buttons shows when the Table View button is selected.
Here is the code:
class MusclesPane extends StatefulWidget {
const MusclesPane({Key? key}) : super(key: key);
@override
State<MusclesPane> createState() => _MusclesPaneState();
}
class _MusclesPaneState extends State<MusclesPane> {
EdgeInsets padding = const EdgeInsets.all(25);
Widget pageSection = const MusclesTableView();
bool tableViewIsActive = true;
bool heatmapDiagramIsActive = false;
@override
Widget build(BuildContext context) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
InkWell(
onTap: () => {
setState(() {
pageSection = const MusclesTableView();
tableViewIsActive = true;
heatmapDiagramIsActive = false;
})
},
child: ViewTypeButton(
title: "Table view", isActive: tableViewIsActive)),
InkWell(
onTap: () => {
setState(() {
pageSection = const MusclesHeatmapDiagram();
heatmapDiagramIsActive = true;
tableViewIsActive = false;
})
},
child: ViewTypeButton(
title: "Heatmap diagram",
isActive: heatmapDiagramIsActive)),
],
),
Padding(padding: padding, child: pageSection),
],
);
}
}
class ViewTypeButton extends StatelessWidget {
const ViewTypeButton({Key? key, required this.title, required this.isActive})
: super(key: key);
final String title;
final bool isActive;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4.0),
child: ElevatedButton(
onPressed: () {},
style: ElevatedButton.styleFrom(
primary: isActive ? Colors.blue : Colors.grey),
child: Text(title)));
}
}
When I click the Heatmap diagram button, nothing happens. Also in the setState functions, I tried using print(heatmapDiagramIsActive)
and print(tableViewIsActive)
to check if the variables change, but nothing gets outputted, so I am not sure if the variables are even changing.
I was thinking it may be due to the scope of the variables. I have tried moving the tableViewIsActive
and heatmapDiagramIsActive
variables outside of the class; I also tried moving them under the build() function but this did not solve the problem.
Any help would be appreciated - I've come back to this issue many times now and I'm still stuck on it. Also, please let me know if there are any Flutter practices I could improve, such as naming conventions or any refactoring suggestions, as I am quite new to Flutter.
As I mentioned in the comment, stacking InkWell
and the ElevatedButton
makes it so that ElevatedButton
is receiving the input but InkWell
isn't. You can get around this by passing in a callback function into the ElevatedButton
and calling the function when the button is tapped as so:
class MusclesPane extends StatefulWidget {
const MusclesPane({Key? key}) : super(key: key);
@override
State<MusclesPane> createState() => _MusclesPaneState();
}
class _MusclesPaneState extends State<MusclesPane> {
EdgeInsets padding = const EdgeInsets.all(25);
Widget pageSection = const MusclesTableView();
bool tableViewIsActive = true;
bool heatmapDiagramIsActive = false;
selectTableView() {
setState(() {
pageSection = const MusclesTableView();
tableViewIsActive = true;
heatmapDiagramIsActive = false;
});
}
selectHeatMapView() {
setState(() {
pageSection = const MusclesHeatMapDiagram();
tableViewIsActive = false;
heatmapDiagramIsActive = true;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 90),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ViewTypeButton(
title: "Table view",
isActive: tableViewIsActive,
callback: selectTableView,
),
ViewTypeButton(
title: "HeatMapDiagram",
isActive: heatmapDiagramIsActive,
callback: selectHeatMapView,
),
],
),
Padding(padding: padding, child: pageSection),
],
);
}
}
class ViewTypeButton extends StatelessWidget {
const ViewTypeButton(
{Key? key,
required this.title,
required this.isActive,
required this.callback})
: super(key: key);
final String title;
final bool isActive;
final Function callback;
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4.0),
child: ElevatedButton(
onPressed: () => callback(),
style: ElevatedButton.styleFrom(
backgroundColor: isActive ? Colors.blue : Colors.grey),
child: Text(title)));
}
}
ElevatedButton
already has InkWell
inside of it, so having an additional is redundant.
Design-wise, using booleans for each button is simple when you have only a few things to select. However, if you want to scale up to having more buttons, you can use an integer as a key to determine if a button is activated or not. You can scale up to as many buttons as you want without having to create endless amounts of boolean variables.
class MusclesPane extends StatefulWidget {
const MusclesPane({Key? key}) : super(key: key);
@override
State<MusclesPane> createState() => _MusclesPaneState();
}
class _MusclesPaneState extends State<MusclesPane> {
EdgeInsets padding = const EdgeInsets.all(25);
Widget pageSection = const MusclesTableView();
int selected = 0;
@override
Widget build(BuildContext context) {
return Column(
children: [
const SizedBox(height: 90),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => setState(() {
selected = 0;
pageSection = const MusclesTableView();
}),
style: ElevatedButton.styleFrom(
backgroundColor: selected == 0 ? Colors.blue : Colors.grey),
child: const Text("Table view")),
ElevatedButton(
onPressed: () => setState(() {
selected = 1;
pageSection = const MusclesHeatmapDiagram();
}),
style: ElevatedButton.styleFrom(
backgroundColor: selected == 1 ? Colors.blue : Colors.grey),
child: const Text("Heatmap diagram")),
],
),
Padding(padding: padding, child: pageSection),
],
);
}
}