Flutter listview, radio, checkbox and textfield realize the list of questionnaire questions

Time:2020-10-27

stayLast articleList view nesting listview scrolling We have done questionnaires and test papers, and know that the list of topics includes: the topics of large questions, the topics of small questions, and the options of small questions. To do this layout, we need to use many components. First, the listview displays the list of topics and options. The options include single choice and multi-choice. Radio and checkbox are used. Some of the questions are short answer questions, so the text box t is used Extfield, you can set multiple lines or single lines.

Specific implementation

It’s too hard to describe the specific logic with language and words. Go directly to the code, and some logic will be acceptedLast articleThat’s omitted here.

//Details initialization defaults to ''
  Map questionnaireDetail = {
    "title": '',
    "startDate": '',
    "endDate": '',
    "remark": '',
  };

  //The data to be displayed in the 'listview'.
  List questionList = new List();

  ScrollController _scrollController = new ScrollController();
//Call the interface to get the details and title option data, the specific code logic is brief
@override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        Title: new text ("questionnaire details"),
      ),
      body: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onTap: () {
          //Click the blank space to cancel textfield focus and touch to fold up the keyboard
          FocusScope.of(context).requestFocus(FocusNode());
        },
        child: new ListView(
          Shrinkwrap: true, // whether to set the length of listview according to the total length of child widgets. The default value is false
          controller: _scrollController,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.all(10.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                   new Text(
                    questionnaireDetail["title"],
                    overflow:  TextOverflow.ellipsis , // display as ellipsis when the text is too long
                    Maxlines: 2, // set to display up to two lines of text
                    style: TextStyle(
                      color:  Color.fromRGBO (0, 0, 0, 1.0), // opacity: opacity
                      fontFamily: 'PingFangBold',
                      fontSize: 15.0,
                    ),
                  ),
                  Container(
                    child: _buildList(),
                  ),
                 ],
              ),
            ),
         ],
        ),
      ),
    );
  }
Widget _buildList() {
    return ListView.builder(
      Shrinkwrap: true, // whether to set the length of listview according to the total length of child widgets. The default value is false
      Physics: new neverscrolllablescrollphysics(), // disable scrolling events of problem list subcomponents
      //Itemcount + 1 is used to display the progressbar in loading and no data at present
      itemCount: questionList.length + 1,
      itemBuilder: (context, index) {
          //List display
          return Container(
            padding: new EdgeInsets.fromLTRB(10, 5, 10, 5),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                 new Text(
                  questionList[index]["title"]),
                Offstage(
                  //Controls whether the most optional maxchoice item component is hidden
                  Offstage: questionlist [index] ['questiontype '] = = multiple choice questions "&&
                          questionList[index]["maxChoice"] != 0
                      ? false
                      : true,
                  Child: new text (')+
                      questionList[index]["maxChoice"].toString() +
                      Item),
                ),
                Questionlist [index] ['questiontype '] = = single choice question "
                    ? _buildRadioChoiceRow(questionList[index])
                    : questionlist [index] ['questiontype '] = = multiple choice questions "
                        ? _buildCheckboxChoiceRow(questionList[index],
                            questionList[index]["maxChoice"])
                        : _buildTextControllerRow(questionList[index]),
              ],
            ),
          );
          },
    );
  }
//Construction of radio multiple choice item list component
  Widget _buildRadioChoiceRow(question) {
    return new ListView.builder(
      Physics: new neverscrolllablescrollphysics(), // disable scrolling events for option list subcomponents
      Shrinkwrap: true, // whether to set the length of listview according to the total length of child widgets. The default value is false
      itemCount: question['options'].length,
      itemBuilder: (context, index) {
        var optionContent = question['options'][index]["optionContent"];
        if (optionContent.indexOf("#OTHER#") == -1) {
          //Print ('not other: '+ optionContent.indexOf ("#OTHER#").toString());
          return _radioListItem(question, optionContent, index, optionContent);
        } else {
          //Other options with input box
          var radioTitle = optionContent.replaceAll("#OTHER#", "");
          //Print ('Other text: '+ radiotitle');
          //Print ('Other: '+ optionContent.indexOf ("#OTHER#").toString());
          return Row(
            children: <Widget>[
              Container(
                width: 150,
                child:
                    _radioListItem(question, optionContent, index, radioTitle),
              ),
              Container(
                width: MediaQuery.of(context).size.width - 200,
                color: const Color(0xFFFFFFFF),
                //Other options input box
                child: _buildTextOtherController(question),
              )
            ],
          );
        }
      },
    );
  }
Widget _radioListItem(question, optionContent, optionIndex, radioTitle) {
    return new Row(
      children: <Widget>[
        //Radiolisttile can also be used here, but this component does not meet our needs, so I wrote the layout later
        new Radio(
          Value: question ['options'] [optionindex] ['id '], // the value is of string type
          Groupvalue: question ['groupvalue '], // selected as value
          onChanged: (val) {
            //Fold up the keyboard
            FocusScope.of(context).requestFocus(FocusNode());
            setState(() {
              question['groupValue'] = val;
              //Print ('selected: '+ val.toString ());
            });
          },
        ),
        Expanded (// the child element text of row needs expanded to implement line break
          child: Text(
            radioTitle,
            Softwrap: true, // Wrap
          ),
        ),
      ],
    );
  }
//Building a checkboxcheckboxmultiple topic option list component
  Widget _buildCheckboxChoiceRow(question, maxChoice) {
    return new ListView.builder(
      Physics: new neverscrolllablescrollphysics(), // disable scrolling events for option list subcomponents
      Shrinkwrap: true, // whether to set the length of listview according to the total length of child widgets. The default value is false
      itemCount: question['options'].length,
      itemBuilder: (context, index) {
        var optionContent = question['options'][index]["optionContent"];

        if (optionContent.indexOf("#OTHER#") == -1) {
          return _checkboxListItem(
              question, maxChoice, optionContent, index, optionContent);
        } else {
          //Other options with input box
          var checkboxTitle = optionContent.replaceAll("#OTHER#", "");
          //Print ('Other text: '+ checkboxtitle');
          return new Row(
            children: <Widget>[
              Container(
                width: 150,
                child: _checkboxListItem(
                    question, maxChoice, optionContent, index, checkboxTitle),
              ),
              Container(
                width: MediaQuery.of(context).size.width - 200,
                color: const Color(0xFFFFFFFF),
                //Other options input box
                child: _buildTextOtherController(question),
              )
            ],
          );
        }
      },
    );
  }
Widget _checkboxListItem(
      question, maxChoice, optionContent, optionIndex, checkboxTitle) {
    return new Row(
      children: <Widget>[
        //You can also use checkboxlisttile here, but this component does not meet our needs, so I wrote the layout myself later
        Checkbox(
          value: question['options'][optionIndex]
              ['ischeck '], // if the value is of bool type, false is not selected
          onChanged: (isCheck) {
            //Fold up the keyboard
            FocusScope.of(context).requestFocus(FocusNode());
            _checkMaxChoise(question, maxChoice, optionIndex, isCheck);
          },
        ),       
        Expanded (// the child element text of row needs expanded to implement line break
          child: Text(
            checkboxTitle,
            Softwrap: true, // Wrap
          ),
        ),
      ],
    );
  }
//The logic of judging the most multiple choice items in maxchoice
  void _checkMaxChoise(question, maxChoice, optionIndex, isCheck) {
    setState(() {
      var optionId = question['options'][optionIndex]['id'];
      question['options'][optionIndex]['isCheck'] = isCheck;
      if (isCheck) {
        //Print ('checked: '+ optionid);
        question['checked'].add(optionId);
        if (maxChoice != 0 && question['checked'].length > maxChoice) {
          question['checked'].remove(optionId);
          question['options'][optionIndex]['isCheck'] = false;
          Showtoast ("the number of current selections has exceeded the maximum number of options in this question");
        }
        //Print ('selected: '+ question ['checked']. Tostring());
      } else {
        question['checked'].remove(optionId);
        //Print ('selected: '+ question ['checked']. Tostring());
      }
    });
  }
//Building input box line short answer component
  Widget _buildTextControllerRow(question) {
    return new Padding(
      padding: const EdgeInsets.all(8.0),
      child: Container(
        color: const Color(0xFFFFFFFF),
        padding: EdgeInsets.only(left: 8.0),
        child: _buildTextField(question['textController']),
    );
  }
//Other input box components for build options
  Widget _buildTextOtherController(question) {
    return _buildTextField(question['textOtherController']);
  }
//Building input box components
  Widget _buildTextField(controller) {
    //The 'textfield' component allows the user to enter text using a hardware or on-screen keyboard.
    return new TextField(
      cursorColor: const Color(0xFFFE7C30),
      cursorWidth: 2.0,
      keyboardType:  TextInputType.multiline // multi line
      decoration: InputDecoration(
        contentPadding: EdgeInsets.all(10.0),
        //The border of a rounded rectangle
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(10.0),
        ),
      ),
      Controller: controller, // controls the text being edited
    );
  }

Effect screen capture

Flutter listview, radio, checkbox and textfield realize the list of questionnaire questions
Flutter listview, radio, checkbox and textfield realize the list of questionnaire questions
Flutter listview, radio, checkbox and textfield realize the list of questionnaire questions

reference material

TextField class
Radio<T> class
Checkbox class
Flitter notes (9) — Radio, radiolisttile
A study tour of flutter

Flutter click the blank space to cancel the textfield focus and collapse the keyboard

Related articles

The listview and refreshindicator components are commonly used to implement pull-up loading and more drop-down refreshing of list pages