Add List to ListView.children dynamically

2019-07-26 01:47发布

问题:

I am trying to dynamically build part of my ListView.children[], yet I fail to push them to already existing array. Here is my simplified code:

  Widget buildNavigationDrawer(BuildContext context) {
    return Drawer(
      child: ListView(
        // Important: Remove any padding from the ListView.
        padding: EdgeInsets.zero,
        children: <Widget>[
          DrawerHeader(...code for header of drawer),

          _buildUserGroups(context).map((Widget item)=> item),

          ListTile(
            leading: Icon(Icons.account_circle),
            title: Text('Settings'),
            onTap: () {
              Navigator.push(
                  context,
                  FadeTransitionRoute(
                      builder: (context) => MainSettingsPage()));
            },
          ),
        ],
      ),
    );
  }

Where _buildUserGroups() function looks like this (simplified):

  List<Widget> _buildUserGroups(BuildContext context) {
    var userGroup = List<Widget>();
    userGroup.add(Text("Users"));

    for (var i = 0; i < 5; i++) {
      userGroup.add(Text("User " + i.toString()));
    }

    return userGroup;
  }

I have tried to use _buildUserGroups(context).forEach(), _buildUserGroups(context).map(), or simply dumping the result like _buildUserGroups(context); yet it fails and says the same error message:

[dart] The element type 'Iterable' can't be assigned to the list type 'Widget'.

I am quite sure the is a way how to do it, but I cannot figure it out. Any help or suggestion in respect to this matter would be highly appreciated as I am quite stuck at the moment.

回答1:

As pointed out by other answers, the _buildUserGroups(context) is the problem, as it returns a list of widgets, resulting in a nested list.

Since Dart 2.3 (introduced to Flutter at Google I/O '19), you can use the spread operator ..., which will unwrap the inner list, so its elements become elements of the outer list:

Widget buildNavigationDrawer(BuildContext context) {
  return Drawer(
    child: ListView(
      children: [
        DrawerHeader(...),
        ..._buildUserGroups(context),
        ListTile(...)
      ],
    ),
  );
}

For more information about what the spread operator does, check out Dart's language tour.


Original answer (before Dart 2.3):

You can wrap the function call in a Column or ExpansionTile.

However, this introduces a new widget into the widget tree. Admittedly, the impact on performance is minimal, but in my opinion, a cleaner solution would be to construct the list of widgets like this:

Widget buildNavigationDrawer(BuildContext context) {
  final items = <Widget>[];
  items.add(DrawerHeader(...));
  items.addAll(_buildUserGroups(context));
  items.add(ListTile(...));

  return Drawer(child: ListView(children: items));
}

If you insist on an inline solution, you can also use .followedBy(...) to add items to the list:

Widget buildNavigationDrawer(BuildContext context) {
  return Drawer(
    child: ListView(
      children: [ DrawerHeader(...) ]
        .followedBy(_buildUserGroups(context))
        .followedBy([ ListTile(...) ])
    ),
  );
}


回答2:

Wrap it with ExpansionTile or Column

Problem:

_buildUserGroups(context).map((Widget item)=> item),

Solution: 1

ExpansionTile(
     title: Text("New List"),
     children:  _buildUserGroups(context).map((Widget item)=> item),
   )

Solution: 2

Column(
     children:  _buildUserGroups(context).map((Widget item)=> item),
   )

Note: Iterable only assigned to Children: because it gets a List<Widget>. You are setting iterable for single child widget, that is why you are getting error.

I hope this solve your problem. Happy Coding!!



回答3:

I over-complicated my thinking so much, I could not see that had a simple solution right in front of me all the time. I ended up wrapping it in the Column widget which solved the problem:

Column(
  crossAxisAlignment: CrossAxisAlignment.start,
  children: _buildUserGroups(context),
),