flutterdart

How do I change colour of Container with code


So I have a list of classes called colourMember which has a gesture detector and a container. The Class has a bool called selected, which should be true when onTap is triggered and false if tapped again. The Container colour should also be red if selected or grey if not selected and this works ok. BUT I only want 1 class to be able to be selected at any one time so when a class ( well the gesture detector in the instance of that class ) is selected if another class is selected the previously selected class should be unselected. My code changes the bool of any previously selected class to false which is what I want but I just can't get the colours of any other selected class to change to grey.

The method which should change the unselected classes to colour grey & bool to false is at the bottom of the code block ( void deselectOtherMembers(int indexOfSelected) ).

BTW if I do a hot reload all the previously selected classes ( red container ) go to grey with only the class that is actually selected ( selected = true ) stays red which is what I want but obviuosly I need it to work at runtime.

Any help appreciated thanks.

class Screen2 extends StatefulWidget {
  Screen2({super.key});
  static const String id = 'screen_2';

  @override
  State<Screen2> createState() => Screen2State();
}

class Screen2State extends State<Screen2> {
  MyHomePageState myHomePage = MyHomePageState();

  static List<colourMember> colourMembersList = [];

  @override
  Widget build(BuildContext context) {
    if (colourMembersList.length < 5) {
      for (int i = 0; i < 4; i++) {
        colourMembersList.add(colourMember(index: i));
      }
    }

    return MaterialApp(
      home: Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(
          title: const Text('screen2 ^^'),
        ),
        body: Column(
          children: [
            SizedBox(
              height: 50,
            ),
            Container(
              height: 280,
              child: ListView(
                scrollDirection: Axis.vertical,
                children: [
                  colourMembersList[0],
                  colourMembersList[1],
                  colourMembersList[2],
                  colourMembersList[3],
                  
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
 
}



class colourMember extends StatefulWidget {
  colourMember({super.key, color, required this.index});
  
  bool selected = false;
  int index;
  Color selectedCol = Colors.redAccent;
  Color nonSelectedColour = Colors.grey;
  Color borderCol = Colors.grey;

  @override
  State<colourMember> createState() => _colourMemberState();
}

class _colourMemberState extends State<colourMember> {
  @override
  Widget build(BuildContext context) {
    return //const //Placeholder();

        GestureDetector(
      onTap: () {
        setState(() {
          if (widget.selected) {
            widget.selected = false;
            widget.borderCol = widget.nonSelectedColour; //Colors.grey;
            print('${widget.index} not selected');
          } else {
            widget.selected = true;
            widget.borderCol = widget.selectedCol; //
            print(' ${widget.index} selected');
            deselectOtherMembers(widget.index);
          }
        });
      },
      child: Container(
        height: 55,
        color: widget.borderCol,
        child: Card(
          child: Row(
            children: [
              Align(
                alignment: Alignment.centerLeft,
                child: Container(
                  width: 50,
                  height: 50,
                  color: Colors.lightBlue,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void deselectOtherMembers(int indexOfSelected) {
    for (int i = 0; i < 4; i++) {
      if (i != indexOfSelected) {
        print(' $i should now be non selected');
        Screen2State.colourMembersList[i].selected = false;
        setState(() {
          Screen2State.colourMembersList[i].borderCol =
              Screen2State.colourMembersList[i].nonSelectedColour;
        });
      }
    } // end of loop
    for (int i = 0; i < 4; i++) {
      print(
          ' $i member, selected status : ${Screen2State.colourMembersList[i].selected} ');
    }
  }
}

Solution

  • It looks like you're close, but the main issue is updating the selection state across all instances in the colourMembersList when one item is selected. Instead of managing the state within each individual colourMember, you can manage the selected index in the parent widget (Screen2) and pass it down, which will simplify updating the colors.

    Solution Steps

    Here’s how you can implement this:

    class Screen2 extends StatefulWidget {
      Screen2({super.key});
      static const String id = 'screen_2';
    
      @override
      State<Screen2> createState() => Screen2State();
    }
    
    class Screen2State extends State<Screen2> {
      int selectedIndex = -1; // Track selected index
    
      static List<ColourMember> colourMembersList = [];
    
      @override
      Widget build(BuildContext context) {
        if (colourMembersList.length < 5) {
          for (int i = 0; i < 4; i++) {
            colourMembersList.add(ColourMember(index: i));
          }
        }
    
        return MaterialApp(
          home: Scaffold(
            resizeToAvoidBottomInset: false,
            appBar: AppBar(
              title: const Text('Screen 2'),
            ),
            body: Column(
              children: [
                SizedBox(height: 50),
                Container(
                  height: 280,
                  child: ListView(
                    scrollDirection: Axis.vertical,
                    children: colourMembersList
                        .map((member) => ColourMemberWidget(
                              index: member.index,
                              isSelected: selectedIndex == member.index,
                              onTap: () => onSelect(member.index),
                            ))
                        .toList(),
                  ),
                )
              ],
            ),
          ),
        );
      }
    
      void onSelect(int index) {
        setState(() {
          selectedIndex = index; // Set the selected index
        });
      }
    }
    
    class ColourMember {
      int index;
      ColourMember({required this.index});
    }
    
    class ColourMemberWidget extends StatelessWidget {
      final int index;
      final bool isSelected;
      final VoidCallback onTap;
    
      ColourMemberWidget({
        required this.index,
        required this.isSelected,
        required this.onTap,
      });
    
      @override
      Widget build(BuildContext context) {
        return GestureDetector(
          onTap: onTap,
          child: Container(
            height: 55,
            color: isSelected ? Colors.redAccent : Colors.grey, // Dynamic color
            child: Card(
              child: Row(
                children: [
                  Align(
                    alignment: Alignment.centerLeft,
                    child: Container(
                      width: 50,
                      height: 50,
                      color: Colors.lightBlue,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    }