【问题标题】:StatefulWidget - FLutterStatefulWidget - 颤振
【发布时间】:2020-10-02 17:25:21
【问题描述】:

我需要编辑此代码,以仅定义一个可变小部件的方式,该小部件可以在每种状态下将其更改为不同的小部件类型。 无论问题及其类型是什么,我都需要能够制作动态表格,我处理它的方式有点复杂且效率不高。 那么对于如何在每个 setState() 上为不同的小部件更改相同的变量有任何想法

    `Column(
                  children: <Widget>[
                    questionText,
                    textCounter > 0 ? textField : SizedBox(),
                    selectCounter > 0 ? selectField : SizedBox()
                  ],
                )),`FutureBuilder(
              future: fetchQuestions(),
              builder: (context, snapshot) {
                if (snapshot.hasData) {
                  for (var i = 0; i < snapshot.data.length; i++) {
                    var temp = snapshot.data[i]['question_value'].toString();
                    var type = snapshot.data[i]['question_type'].toString();
                    questionsList.add(temp);
                    typeList.add(type);
                  }

                  return Align(
                    alignment: Alignment.bottomRight,
                    child: RaisedButton(
                      onPressed: () {
                        changeQuest(questionsList, typeList,
                            snapshot.data.length, snapshot.data);
                      },
                      child: Text('next'),
                    ),
                  );
                } else
                  return Center(child: CircularProgressIndicator());
              },
            ),

    changeQuest(List questions, List type, length, data) {
    setState(() {
      textCounter = 0;
      selectCounter = 0;
      integerCounter = 0;
      if (counter < length) {
        questionText = Text(questions[counter]);
        if (type[counter] == 'Integer') {
          textCounter++;
          textField = TextFormField(
            decoration: new InputDecoration(labelText: "Enter your number"),
            keyboardType: TextInputType.number,
            inputFormatters: <TextInputFormatter>[
              WhitelistingTextInputFormatter.digitsOnly
            ], // Only numbers can be entered
          );
        } else if (type[counter] == 'Text') {
          textCounter++;
          textField = TextFormField(
            decoration: new InputDecoration(labelText: "Enter a text"),
            keyboardType: TextInputType.text,
          );
        } else if (type[counter] == 'Select') {
          selectCounter++;
          for (var i = 0; i < data[counter]['answers'].length; i++) {
            answersList
                .add(data[counter]['answers'][i]['answer_value'].toString());
          }
          dropDownValue = answersList[0];
          selectField = DropdownButton<String>(
            value: dropDownValue,
            icon: Icon(Icons.arrow_downward),
            iconSize: 24,
            elevation: 16,
            style: TextStyle(color: Colors.deepPurple),
            underline: Container(
              height: 2,
              color: Colors.deepPurpleAccent,
            ),
            onChanged: (value) {
              setState(() {
               dropDownValue = value;
              });
            },
            items: answersList
                .map<DropdownMenuItem<String>>((String value) {
              return DropdownMenuItem<String>(
                value: value,
                child: Text(value),
              );
            }).toList(),
          );
          print (dropDownValue);
        }
      }

      counter++;
    });
  }

【问题讨论】:

  • 根据某些条件调用不同的小部件。假设下拉==true?调用下拉小部件,文本字段 ==true?调用文本字段小部件..

标签: flutter dart widget setstate dynamicform


【解决方案1】:

正如@proversion 在 cmets 中所说,您可以检查小部件树,如果条件返回 true 或 false。

在输入孩子之前,您可以使用内联 if 语句进行检查,如下所示: questionType == 'dropdown' ? (Widget for True) : (Widget for False)

或者如果您必须进行复杂的检查,我会在小部件的return 之前的build 方法中执行此操作,并在那里设置一个布尔值,它代表您的检查结果。 然后您可以在小部件树中使用此值(例如:isTrue),如isTure ? (Widget for True) : (Widget for False)

这是一个示例代码,应该可以工作。

import 'package:flutter/material.dart';

class WidgetWithDifferentChildren extends StatefulWidget {
  @override
  _WidgetWithDifferentChildrenState createState() =>
      _WidgetWithDifferentChildrenState();
}

class _WidgetWithDifferentChildrenState
    extends State<WidgetWithDifferentChildren> {
  String questionType = '';
  String dropdownValue = 'SelectItem';
  String textValue = '';
  TextEditingController txtCtrl = TextEditingController();

  @override
  void dispose() {
    // TODO: implement dispose when using TextEditingController
    txtCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: questionType == ''
          ? Text('no Question Type')
          : questionType == 'dropdown'
              ? DropdownButton<String>(
                  value: dropdownValue,
                  onChanged: (String newValue) {
                    // Do something with the new Value
                    print('New DropDown value = $newValue');
                    setState(() {
                      dropdownValue = newValue;
                    });
                  },
                  items: <String>[
                    'SelectItem',
                    'Item 1',
                    'Item 2',
                    'Item 3',
                  ].map<DropdownMenuItem<String>>((String value) {
                    return DropdownMenuItem<String>(
                      value: value,
                      child: new Text(value),
                    );
                  }).toList(),
                )
              : questionType == 'textfield'
                  ? TextFormField(
                      controller: txtCtrl,
                      onChanged: (value) {
                        // Do something with the new Value
                        print('New TextField value = $value');
                        setState(() {
                          textValue = value;
                        });
                      },
                    )
                  : Text('Question Type does not match'),
    );
  }
}

更新

根据。对于您提供的代码,您可能需要检查以下内容。我创建了一个单独的类,它将为问题返回正确的小部件。只需将typedropDownList 附加到函数即可。

一般我建议将问题和相应的答案存储在同一个数组中,这将是一个简单的函数调用,如getInputWidget(type:question[i].type, dropDownList:question[i].dropDownList)

以上示例的源代码

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class WidgetWithDifferentChildren extends StatefulWidget {
  @override
  _WidgetWithDifferentChildrenState createState() =>
      _WidgetWithDifferentChildrenState();
}

class _WidgetWithDifferentChildrenState
    extends State<WidgetWithDifferentChildren> {
  String questionType = '';
  String inputValue = '';
  List<String> answers = [];
  int questionID = 0;
  TextEditingController txtCtrl = TextEditingController();

  List<Map<String, String>> questionList = [
    {'question_value': 'text question ', 'question_type': 'text'},
    {'question_value': 'number question ', 'question_type': 'number'},
    {'question_value': 'select question ', 'question_type': 'select'},
    {'question_value': 'last question ', 'question_type': 'text'},
  ];
  List<String> dropDownList = [
    'Select an Item',
    'Answer A',
    'Answer B',
    'Answer C',
  ];

  @override
  void dispose() {
    // TODO: implement dispose when using TextEditingController
    txtCtrl.dispose();
    super.dispose();
  }

  Widget getInputWidget({@required String type, List<String> dropDownList}) {
    Widget inputW;
    if (type == 'number' || type == 'text') {
      inputW = TextFormField(
        controller: txtCtrl,
        decoration: new InputDecoration(labelText: "Enter a $type"),
        keyboardType:
            type == 'text' ? TextInputType.text : TextInputType.number,
        inputFormatters: <TextInputFormatter>[
          type == 'text'
              ? LengthLimitingTextInputFormatter(50)
              : WhitelistingTextInputFormatter.digitsOnly
        ], // Only numbers can be entered
        onChanged: (value) {
          setState(() {
            inputValue = value;
          });
        },
      );
    } else if (type == 'select') {
      if (inputValue.length == 0) {
        // set the input Value for the first time
        inputValue = dropDownList[0];
      }
      inputW = DropdownButton<String>(
        value: inputValue,
        icon: Icon(Icons.arrow_downward),
        iconSize: 24,
        elevation: 16,
        style: TextStyle(color: Colors.deepPurple),
        underline: Container(
          height: 2,
          color: Colors.deepPurpleAccent,
        ),
        onChanged: (value) {
          setState(() {
            inputValue = value;
          });
        },
        items: dropDownList.map<DropdownMenuItem<String>>((String value) {
          return DropdownMenuItem<String>(
            value: value,
            child: Text(value),
          );
        }).toList(),
      );
    }

    return inputW;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 30),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            RaisedButton(
              onPressed: () {
                setState(() {
                  answers.add(inputValue);
                  inputValue = '';
                  txtCtrl.clear();
                  questionID = questionID + 1;
                });

                // unfocus to close the Keyboard
                // conrtibution to: https://flutterigniter.com/dismiss-keyboard-form-lose-focus/
                FocusScopeNode currentFocus = FocusScope.of(context);
                if (!currentFocus.hasPrimaryFocus) {
                  currentFocus.unfocus();
                }
              },
              child: Text('next'),
            ),
            getInputWidget(
                type: questionList[questionID]['question_type'],
                dropDownList: dropDownList),
            Divider(thickness: 2),
            Text('You enter: $inputValue'),
            Divider(thickness: 2),
            Text('Your answers are:'),
            Flexible(
              child: ListView.builder(
                  itemCount: answers.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text('$index. ${answers[index]}'),
                    );
                  }),
            ),
          ],
        ),
      ),
    );
  }
}

【讨论】:

  • 这是一个很好的做法,但我真正需要的是更改 setState() 上的小部件类型,这是不可行的......我用他们的类型保存了问题在一个列表中,在每个状态下,我都会从列表中显示一个带有特定小部件的问题......我会更新代码也许你会明白我的意思......我的代码正在工作,但实际上变得越来越复杂,并且我相信还有另一种方法来处理它
  • 能否提供一些代码。我想,当您更改 setState() 中的“条件值”时,您可以更改小部件及其类型。被变化值“感染”的完整小部件树应该重建。
  • 在提供的代码中,我为每种类型定义了一个小部件,并且我在每个状态都更改它,但实际上我想要做的是更改相同的变量,这将使生活更容易,但错误是 TextField 不是 DropDownButton 的子类型
  • @MoussaNasreddine:我用一些虚拟值和一个返回正确小部件的函数更新了代码。也许这对您来说是一个很好的起点。
  • 这很有帮助,实际上我错过了调用小部件的想法,它的类型会相应地改变......现在一切正常......谢谢
猜你喜欢
  • 2022-01-04
  • 2019-11-03
  • 2019-04-07
  • 2019-08-29
  • 1970-01-01
  • 1970-01-01
  • 2021-04-05
  • 2021-07-04
  • 2019-01-10
相关资源
最近更新 更多