简体   繁体   English

For循环下的小部件未在颤动中呈现

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM