StatefulWidget - FLutter
StatefulWidget - FLutter
我需要编辑此代码,以仅定义一个可变小部件的方式,该小部件可以在每种状态下更改为不同的小部件类型。
无论问题及其类型是什么,我都需要能够制作动态表格,我处理它的方式在某种程度上是复杂且效率低下的。
那么有没有关于如何在每个 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++;
});
}
正如@proversion 在评论中所说,如果条件 return 为真或假,您可以检查小部件树。
在输入 child 之前,您可以使用内联 if-statement 进行检查,如下所示:
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'),
);
}
}
更新
符合。对于您提供的代码,您可能需要检查以下内容。我创建了一个单独的 class,它将 return 问题的正确小部件。只需将 type
和附加的 dropDownList
传递给函数即可。
一般我建议将问题和相应的答案存储在同一个数组中,这将是像 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()
上为不同的小部件更改相同变量的想法? `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++;
});
}
正如@proversion 在评论中所说,如果条件 return 为真或假,您可以检查小部件树。
在输入 child 之前,您可以使用内联 if-statement 进行检查,如下所示:
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'),
);
}
}
更新
符合。对于您提供的代码,您可能需要检查以下内容。我创建了一个单独的 class,它将 return 问题的正确小部件。只需将 type
和附加的 dropDownList
传递给函数即可。
一般我建议将问题和相应的答案存储在同一个数组中,这将是像 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]}'),
);
}),
),
],
),
),
);
}
}