简体   繁体   中英

Iterating through a list to render multiple widgets in Flutter?

I have a list of strings defined like this:

var list = ["one", "two", "three", "four"]; 

I want to render the values on the screen side by side using text widgets. I have attempted to use the following code to attempt this:

for (var name in list) {
   return new Text(name);
}

However, when I run this code, the for loop only runs once and there is only one text widget that gets rendered that says one (the first item in the list). Additionally, when I add a log message inside my for loop, it gets triggered once as well. Why isn't my for loop looping based on the length of the list? It seems to run only once and then quit.

Basically when you hit 'return' on a function the function will stop and will not continue your iteration, so what you need to do is put it all on a list and then add it as a children of a widget

you can do something like this:

  Widget getTextWidgets(List<String> strings)
  {
    List<Widget> list = new List<Widget>();
    for(var i = 0; i < strings.length; i++){
        list.add(new Text(strings[i]));
    }
    return new Row(children: list);
  }

or even better, you can use .map() operator and do something like this:

  Widget getTextWidgets(List<String> strings)
  {
    return new Row(children: strings.map((item) => new Text(item)).toList());
  }

It is now possible to achieve that in Flutter 1.5 and Dart 2.3 by using a for element in your collection.

var list = ["one", "two", "three", "four"]; 

child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
             for(var item in list ) Text(item)
          ],
        ),    

This will display four Text widgets containing the items in the list.
NB. No braces around the for loop and no return keyword.

The Dart language has aspects of functional programming, so what you want can be written concisely as:

List<String> list = ['one', 'two', 'three', 'four'];
List<Widget> widgets = list.map((name) => new Text(name)).toList();

Read this as "take each name in list and map it to a Text and form them back into a List ".

For googler, I wrote a simple Stateless Widget containing 3 method mentioned in this SO. Hope this make it easier to understand.

import 'package:flutter/material.dart';

class ListAndFP extends StatelessWidget {
  final List<String> items = ['apple', 'banana', 'orange', 'lemon'];

  //  for in (require dart 2.2.2 SDK or later)
  Widget method1() {
    return Column(
      children: <Widget>[
        Text('You can put other Widgets here'),
        for (var item in items) Text(item),
      ],
    );
  }

  // map() + toList() + Spread Property
  Widget method2() {
    return Column(
      children: <Widget>[
        Text('You can put other Widgets here'),
        ...items.map((item) => Text(item)).toList(),
      ],
    );
  }

  // map() + toList()
  Widget method3() {
    return Column(
      // Text('You CANNOT put other Widgets here'),
      children: items.map((item) => Text(item)).toList(),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: method1(),
    );
  }
}

To loop through a for-loop with multiple widgets in children,

children: [
  for(int i = 0; i < item.length; i++) ...[
    Widget1,
    Widget2,
    ...
  ],
],

The simplest way is to map your list inside a Row or a Column widget :

var list = ["one", "two", "three", "four"]; 

Widget build(BuildContext context) {
    return Row(children: List.from(list.map((name) => Text(name))));
}

All you need to do is put it in a list and then add it as the children of the widget.

you can do something like this:

Widget listOfWidgets(List<String> item) {
  List<Widget> list = List<Widget>();
  for (var i = 0; i < item.length; i++) {
    list.add(Container(
        child: FittedBox(
          fit: BoxFit.fitWidth,
          child: Text(
            item[i],
          ),
        )));
  }
  return Wrap(
      spacing: 5.0, // gap between adjacent chips
      runSpacing: 2.0, // gap between lines
      children: list);
}

After that call like this

child: Row(children: <Widget>[
   listOfWidgets(itemList),
])

在此处输入图片说明

You can use ListView to render a list of items. But if you don't want to use ListView, you can create a method which returns a list of Widgets (Texts in your case) like below:

var list = ["one", "two", "three", "four"];

    @override
    Widget build(BuildContext context) {
      return new MaterialApp(
          home: new Scaffold(
        appBar: new AppBar(
          title: new Text('List Test'),
        ),
        body: new Center(
          child: new Column( // Or Row or whatever :)
            children: createChildrenTexts(),
          ),
        ),
      ));
    }

     List<Text> createChildrenTexts() {
    /// Method 1
//    List<Text> childrenTexts = List<Text>();
//    for (String name in list) {
//      childrenTexts.add(new Text(name, style: new TextStyle(color: Colors.red),));
//    }
//    return childrenTexts;

    /// Method 2
    return list.map((text) => Text(text, style: TextStyle(color: Colors.blue),)).toList();
  }

You can make use of ListView.Builder() if you are receiving response from a http request that as an array

List items = data;

Container(
  child: ListView.builder(
    shrinkWrap: true,
    itemCount: items.length,
    itemBuilder: (BuildContext context, int index){
      return Container(
        child: Text(
          items[index]['property']
        ),
      );
    },
  ),
);

Where data is content returned from a http request using post or get item is the array ' property ' is one of the property of each item in the array assuming your are receiving back a list of objects

An easier approach may be to use expand :

For example

var paragraphs = ['Para1','Para2','Para3'];

Somewhere in your widget tree you can do this:

...paragraphs.expand((value) => [
SizedBox(
      height: 10.0,
    ),
    Text(
      value,
      // Add some styling if necessary
    ),
    SizedBox(
      height: 20.0,
    ),
  ]),

Here expand returns an iterable of widgets which is then spread using the Spread Operator( ... ).

一条线

Column( children: list.map((e) => Text(e)).toList() )

when you return some thing, the code exits out of the loop with what ever you are returning.so, in your code, in the first iteration, name is "one" . so, as soon as it reaches return new Text(name) , code exits the loop with return new Text("one") . so, try to print it or use asynchronous returns.

Below works for me using the collection package :

https://pub.dev/packages/collection

children: <Widget>[
                ...languages.mapIndex((idx, item) {
                  return InkWell(
                      child: CustomCheckbox(Skill(item, _languageSelections[idx])),
                      onTap: () {
                        setState(() {
                          _languageSelections[idx] = !_languageSelections[idx];
                        });
                      });
                })
              ],

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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