[英]Widgets under For loop not rendering in flutter
I am building a points calculator application in flutter.我正在颤振中构建一个积分计算器应用程序。 I used drop down button for user to select number of players.
我使用下拉按钮让用户选择玩家数量。 and I used for loop to create number of textformfield for user to fill the names of the player.
并且我使用 for 循环为用户创建多个 textformfield 以填充播放器的名称。 When I perform hot reload, I can see the dropdown menu is working fine but the code under for loop is not rendering.
当我执行热重载时,我可以看到下拉菜单工作正常,但 for 循环下的代码没有呈现。 Here is my code:
这是我的代码:
import 'package:flutter/material.dart';
class NewGame extends StatefulWidget {
@override
_NewGameState createState() => _NewGameState();
}
class _NewGameState extends State<NewGame> {
int? numberOfPlayers = 4;
var arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New Game"),
),
body: Row(children: [
Container(
height: 20,
width: 300,
child: Text(
"enter the number of players",
style: TextStyle(
fontSize: 20,
color: Colors.deepPurple,
),
),
),
DropdownButton<int>(
hint: Text("Pick"),
value: numberOfPlayers,
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(value.toString()),
);
}).toList(),
onChanged: (int? newVal) {
setState(() {
numberOfPlayers = (newVal);
if (newVal != 0) {
for (var i = 0; i < ((newVal as int) - 1); i++) {
String stringI = i.toString();
Column(
children: [
SizedBox(
height: 20,
),
TextFormField(
decoration: InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter player ' + stringI + 'name'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
)
],
);
}
}
});
})
]));
}
}
Use ListView.builder
使用
ListView.builder
My dirty-answer:我的肮脏答案:
import 'package:flutter/material.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
// body: Center(
body: NewGame(),
// ),
),
);
}
}
class NewGame extends StatefulWidget {
@override
_NewGameState createState() => _NewGameState();
}
class _NewGameState extends State<NewGame> {
int? numberOfPlayers = 4;
var arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
// List<Widget> dd = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New Game"),
),
body: Row(children: [
Container(
height: 20,
width: 300,
child: Text(
"enter the number of players",
style: TextStyle(
fontSize: 20,
color: Colors.deepPurple,
),
),
),
DropdownButton<int>(
hint: Text("Pick"),
value: numberOfPlayers,
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(value.toString()),
);
}).toList(),
onChanged: (int? newVal) {
setState(() {
print(newVal);
numberOfPlayers = newVal;
});
}),
Expanded(
child: ListView.builder(
itemCount: numberOfPlayers,
itemBuilder: (context, i) {
return makeTextInput();
}))
]));
}
TextFormField makeTextInput() {
return TextFormField(
decoration: InputDecoration(
border: UnderlineInputBorder(), labelText: 'Enter player name'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
);
}
}
EDIT to make original answer work编辑以使原始答案起作用
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: NewGame(),
);
}
}
class NewGame extends StatefulWidget {
@override
_NewGameState createState() => _NewGameState();
}
class _NewGameState extends State<NewGame> {
int? numberOfPlayers = 4;
var arr = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
List<Widget> data = []; // <-- nothing here so nothing will be placed inside column
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("New Game"),
),
body: Center(
child: Column(children: [
Container(
height: 30,
width: 300,
child: Text(
"enter the number of players",
style: TextStyle(
fontSize: 20,
color: Colors.deepPurple,
),
),
),
DropdownButton<int>(
hint: Text("Pick"),
value: numberOfPlayers,
items: <int>[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((int value) {
return new DropdownMenuItem<int>(
value: value,
child: new Text(value.toString()),
);
}).toList(),
onChanged: (int? newVal) {
data = []; // <-- it is need to be reset becasue you will end with adding more and more items
setState(() {
numberOfPlayers = (newVal);
if (newVal != 0) {
for (var i = 0; i < ((newVal as int)); i++) {
String stringI = i.toString();
data.add(createTextField(
stringI)); // <--here when magic happens.
}
}
});
}),
Column( // column is now attached to widget tree from begining. You only need to update items inside
children:
data, //when data is changes, column is forced to rebuild
)
]),
));
}
}
Widget createTextField(String stringI) {
// its return only TextField
return Center(
child: Padding(
padding: EdgeInsets.all(8),
child: TextFormField(
decoration: InputDecoration(
border: UnderlineInputBorder(),
labelText: 'Enter player ${int.parse(stringI) + 1} name'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
),
);
}
I'll distill and refactor your code to try to make it clearer why your code doesn't do what you intend:我将提炼和重构您的代码,以尝试更清楚地说明为什么您的代码没有按照您的意图执行:
class _NewGameState extends State<NewGame> {
// ...
@override
Widget build(BuildContext context) {
return Scaffold(
// ...
body: Row(
children: [
// ...
DropdownButton<int>(
// ...
onChanged: onChanged,
),
],
),
);
}
void onChanged(int? newVal) {
void setStateCallback() {
// ...
for (var i = 0; i < ((newVal as int) - 1); i++) {
// ...
Column(
children: [
// ...
],
);
}
}
setState(setStateCallback);
}
}
You can see that setStateCallback
creates a Column
object on each iteration but never does anything with it , and there's no way for those widgets to be returned from setStateCallback
to be included in the widget tree returned by build
.您可以看到
setStateCallback
在每次迭代时创建一个Column
对象,但从不对其进行任何操作,并且无法将setStateCallback
返回的这些小部件包含在build
返回的小部件树中。
One way (but not necessarily the best way) to fix it would be to add a List<Column>
member to your _NewGameState
, make your setState
callback explicitly add Column
s to that list, and then make your build
method include that list somewhere.修复它的一种方法(但不一定是最好的方法)是将
List<Column>
成员添加到您的_NewGameState
,使您的setState
回调显式地将Column
s 添加到该列表,然后使您的build
方法在某处包含该列表。 I don't know exactly where in your widget tree you intend for those Column
widgets to go, but, for example:我不知道您打算让这些
Column
小部件在小部件树中的确切位置,但是,例如:
class _NewGameState extends State<NewGame> {
// ...
final columns = <Column>[]; // <--
@override
Widget build(BuildContext context) {
return Scaffold(
// ...
body: Row(
children: [
// ...
DropdownButton<int>(
// ...
onChanged: (int? newVal) {
setState(setStateCallback);
},
),
...columns, // <--
],
),
);
}
void onChanged(int? newVal) {
void setStateCallback() {
// ...
columns.clear(); // <--
for (var i = 0; i < ((newVal as int) - 1); i++) {
// ...
columns.add( // <--
Column(
children: [
// ...
],
),
);
}
}
setState(setStateCallback);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.