1

I am trying to build a multiple choice Row from a list of Container widgets (wrapped in GestureDetector for onTap). When the user clicks on one of the Container widgets in the Row, that Container widget changes color (to blue) to reflect the user's selection. If the user then taps a different Container widget in the Row, THAT widget changes color to reflect the user's selection, and all others are reset to the initial color (white). Additionally, the user's choice (index?) must be retrievable.

All I have been able to do so far is create a Row of Container widgets that change color on tap, but function independently of one another. That is to say that the user may click on multiple selections, which is bad. I need some advice on how to break through to next level of functionality, where only one container may be selected and the selected value is passed-out.

Cheers, T

//choice button
class ChoiceButton extends StatefulWidget {
  final String label;
  final bool isPressed;

  const ChoiceButton({
    Key key,
    this.isPressed = false,
    this.label,
  }) : super(key: key);

  @override
  _ChoiceButtonState createState() => _ChoiceButtonState();
}

class _ChoiceButtonState extends State<ChoiceButton> {
  bool _isPressed;

  @override
  void initState() {
    super.initState();
    _isPressed = widget.isPressed;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {
          _isPressed = !_isPressed;
        });
      },
      child: Container(
        height: 80,
        width: 80,
        decoration: BoxDecoration(
          color: _isPressed ? Colors.blue : Colors.transparent,
          border: Border.all(
            color: Colors.blue,
            width: 80 * 0.05,
          ),
        ),
        child: Center(
          child: Text(
            widget.label,
            style: TextStyle(
              fontSize: 12,
              fontWeight: FontWeight.bold,
              color: _isPressed ? Colors.white : Colors.blue,
            ),
            textAlign: TextAlign.center,
          ),
        ),
      ),
    );
  }
}

//choice row
class ChoiceRow extends StatefulWidget {
  @override
  _ChoiceRowState createState() => _ChoiceRowState();
}

class _ChoiceRowState extends State<ChoiceRow> {
  bool isPressed = false;
  String classChoice = '';

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: \[
        SizedBox(width: 30),
        ChoiceButton(
          isPressed: isPressed,
          label: 'A',
        ),
        SizedBox(width: 10),
        ChoiceButton(
          isPressed: isPressed,
          label: 'B',
        ),
        SizedBox(width: 10),
        ChoiceButton(
          isPressed: isPressed,
          label: 'C',
        ),
      \],
    );
  }
}

Result

tap
  • 11
  • 1
  • 5

1 Answers1

1

I am providing two answers for this .... Use whichever one you prefer.

  1. The easy one :

So Flutter provides a widget named ToggleButton which will satisfy all the needs mentioned above. Follow this documentation for more information about this widget.

You can add your custom ChoiceButton design in the widget list of the toggle button and with some tweak you will also be able to achieve your ChioceRow design too.

  1. Now if you are someone who like to make everything from scratch (like me :P) then I have made some changes in the code you provided above which will satisfy all your needs. Below is the edited code.
class ChoiceRow extends StatefulWidget {
  @override
  _ChoiceRowState createState() => _ChoiceRowState();
}

class _ChoiceRowState extends State<ChoiceRow> {

  List<bool> isPressedList = [false,false,false];

  String classChoice = '';

  @override
  Widget build(BuildContext context) {

    print("Status L $isPressedList");

    return Row(
      mainAxisAlignment: MainAxisAlignment.start,
      children: [
        SizedBox(width: 30),
        GestureDetector(
          onTap: (){
            print("Hello");
            setState(() {
              isPressedList[0] = true;
              isPressedList[1] = false;
              isPressedList[2] = false;
            });
          },
          child: ChoiceButton(
            isPressed: isPressedList[0],
            label: 'A',
          ),
        ),
        SizedBox(width: 10),
        GestureDetector(
          onTap: (){
            setState(() {
              isPressedList[0] = false;
              isPressedList[1] = true;
              isPressedList[2] = false;
            });
          },
          child: ChoiceButton(
            isPressed: isPressedList[1],
            label: 'B',
          ),
        ),
        SizedBox(width: 10),
        GestureDetector(
          onTap: (){
            setState(() {
              isPressedList[0] = false;
              isPressedList[1] = false;
              isPressedList[2] = true;
            });
          },
          child: ChoiceButton(
            isPressed: isPressedList[2],
            label: 'C',
          ),
        ),
      ],
    );
  }
}







class ChoiceButton extends StatelessWidget {

  final String label;
  final bool isPressed;

  ChoiceButton({this.label,this.isPressed});


  @override
  Widget build(BuildContext context) {
    return Container(
      height: 80,
      width: 80,
      decoration: BoxDecoration(
        color: isPressed ? Colors.blue : Colors.transparent,
        border: Border.all(
          color: Colors.blue,
          width: 80 * 0.05,
        ),
      ),
      child: Center(
        child: Text(
          label,
          style: TextStyle(
            fontSize: 12,
            fontWeight: FontWeight.bold,
            color: isPressed ? Colors.white : Colors.blue,
          ),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }
}

CHANGES :

  1. I made a list (onPressedList) which keeps track of the current status of the toggle buttons (Like which is on and which others are off).

  2. I moved the GestureDetector wrap on the button in the ChoiceRow class. This is because it will be difficult to pass the onTap result from ChoiceButton to ChoiceRow. (Now if you want the gesture detector in the Button class itself then you can make the list global or a static value in a completely different so that it can be properly accessed)

  3. I made the ChoiceButton class Stateless as there is no need to keep it Stateful now.

The two things I did were adding a list that tracks the current status of the toggle buttons and when one toggle button is active all the others will be deactivated.

Now it is working as you mentioned above and you will also be able to keep track of the current status of all the buttons through "isPressedList".

GLHF :)

Sarvesh Dalvi
  • 2,260
  • 2
  • 14
  • 30
  • Genius! I am team build-it-yourself, so I greatly appreciate your code snippet as an alternative to the ToggleButtons class. Suppose this row lives on a screen and I want the user's responses to be passed to the next screen, how can we access the selections from the class you have just written? – tap Oct 21 '20 at 19:15
  • Another way is to create a completely different class ... say we name it : class IsPressedListData. ........ Then you can keep your list in that class and make it static........ Like this : class IsPressedListData{ static List isPressedList = [false,false,false]} ......... Now say if you want to get status of button 'A' in any other class in your code, you can call it like : IsPressedListData.isPressedList[0]. – Sarvesh Dalvi Oct 21 '20 at 19:42
  • If you are not able to understand what I wrote in above comment I will edit my answer with the solution I provided in comment( creating a different class for that list) – Sarvesh Dalvi Oct 21 '20 at 19:47
  • Hmm, you lost me a little bit with making a new class IsPressedListData... Can you show an example? And how the result of the row is then accessible outside the widget? – tap Oct 22 '20 at 19:39