18

I have a list of Raised buttons, I want the background color of the selected button to change in its onPressed()

I tried changing the color in setState but it doesn't do anything.

This is the function that generates the list of Buttons

List<Widget> _makeZoneList(List<Zone> zones) {
    List<Widget>Buttons = new List();
    for (int i = 0; i < zones.length; i++) {
      Buttons.add(RaisedButton(
        color: zones[i].isSelected ? AppColors.primaryColor : AppColors.white,
        onPressed: () {
          setState(() {
            if (zones[i].isSelected){
              zones[i].isSelected = false;
            }
            else{
              zones[i].isSelected = true;
            }
            print(zones[i].isSelected.toString());
          });
        },
        child: Text(zones.elementAt(i).text)
      ));
    }
    return Buttons;
  }

This is where I call the function

Widget _zoneBody() {
    return Padding(
        padding: EdgeInsets.all(32),
        child: StreamBuilder<List<Zone>>(
            stream: GetterBloc.zonesStream,
            builder: (context, snapshot) {
              if (snapshot.connectionState == ConnectionState.waiting) {
                return new Container();
              } else {
                if (snapshot.hasData) {
                     return Wrap(
                          spacing: 6.0, // gap between adjacent chips
                          children: _makeZoneList(snapshot.data));

                } else {
                  return new Container();
                }
              }
            }));
  }

When I press any button, its isSelected value changes but the background doesn't change accordingly

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Ghada Shebl
  • 539
  • 2
  • 6
  • 12

4 Answers4

40

Updated answer (ElevatedButton)

Since RaisedButton is now deprecated, use ElevatedButton:

enter image description here

Code:

class _MyState extends State<MyPage> {
  bool _flag = true;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => setState(() => _flag = !_flag),
          child: Text(_flag ? 'Red' : 'Green'),
          style: ElevatedButton.styleFrom(
            backgroundColor: _flag ? Colors.red : Colors.teal, // This is what you need!
          ),
        ),
      ),
    );
  }
}

Old answer

It was difficult to implement your code because of undefined classes and variable, however I created a small example which will help you what you are looking for.

List<bool> _list = [true, false, true, false];

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(title: Text("Title")),
    body: ListView(children: _buildButtons()),
  );
}

List<Widget> _buildButtons() {
  List<Widget> listButtons = List.generate(_list.length, (i) {
    return RaisedButton(
      color: _list[i] ? Colors.green : Colors.red,
      onPressed: () {
        setState(() {
          _list[i] = !_list[i];
        });
      },
      child: Text("Button #${i}"),
    );
  });
  return listButtons;
}

Output:

enter image description here

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
2

Unless I'm mistaken, what you're trying to do is already handled by flutter. I think all you have to do is set the hightlightColor of the button and when it is pressed it will change to that color. And you could set this into the theme for your entire application so that all buttons behave the same rather then setting it for each individual button.

However, there's also a reason why what you're doing isn't working. You haven't included quite enough code for me to tell, but I believe the reason why what you're doing isn't working is that you have a List of data that you're mutating when the button is pressed (i.e. zones[i].isSelected = false;). You're doing that in a setState, but the way that flutter checks whether something needs rebuilding is by doing an equality compare on the State's members (i.e. it will check whether zones == zones).

Because 'zones' is just a list, and is actually the same list in for the old state and the new state, flutter will assume nothing has changed and won't bother rebuilding.

There's two easy ways to get around this. One would be to make a copy of the list each time it is modified and set the zones member to that, so that when flutter does the compare old.zones != new.zones. The other way would be to keep a separate object that you change each time the list is modified (I tend to use an integer called changeCounter that I increment each time the list changes) as that way you can 'fool' flutter into rebuilding.

rmtmckenzie
  • 37,718
  • 9
  • 112
  • 99
2

Here is a very simple approach.

bool isButtonPressed = false;

RaisedButton(
            color: isButtonPressed ? Colors.green : Colors.red,
            onPressed: () {
              setState(() {
                isButtonPressed =!isButtonPressed;
              });
            },
          ),
1

What you can do is create a "color" property (not required to set any value for it) in Zone class. Then set the color property of RaisedButton to zone.color ??= AppColors.primaryColor.

And now, inside onPressed function you can check if !zone.isSelected then set zone.color = Colors.white.

Below is the implementation for creating RaisedButton. You can also check full implementation here

List<Widget> _createButton(BuildContext context, List<Zone> zones) {
    return zones.map((Zone zone) {
      return RaisedButton(
        onPressed: () {
          setState(() {
            if (!zone.isSelected) {
              zone.color = AppColors.white;
            }
          });
        },
        color: zone.color ??= Theme.of(context).primaryColor,
        child: Text(zone.text),
      );
    }).toList();
  }
Ankit
  • 336
  • 3
  • 4