146

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.

rakeshdas
  • 2,363
  • 5
  • 20
  • 28

12 Answers12

231

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());
  }
Rap
  • 6,851
  • 3
  • 50
  • 88
Rizky Andriawan
  • 3,376
  • 3
  • 9
  • 10
145

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.

nonybrighto
  • 8,752
  • 5
  • 41
  • 55
54

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".

Evelyn
  • 2,588
  • 3
  • 22
  • 47
Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • 2
    I think that this is a better answer than the one that is marked as good, since you can actually use this directly in the build function without having to call a function that returns a widget – Jose Jet Oct 31 '18 at 12:47
  • how could I get the index inside that .map? – Dani Feb 10 '20 at 14:51
  • 1
    @dani you can't with map, but you can with the for loop in rizky's answer – Richard Heap Feb 10 '20 at 14:59
  • list.map((name) => new Text(name)).toList(); I forgot to add toList() function at the end. Thanks bro :) – Kamlesh May 18 '21 at 07:01
49

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(),
    );
  }
}
user2875289
  • 2,799
  • 1
  • 22
  • 25
15

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

children: [
  for(int i = 0; i < item.length; i++) ...[
    Widget1,
    Widget2,
    ...
  ],
],
ltk
  • 994
  • 7
  • 9
  • 1
    Thats useful if you need index to check selected item for example. – Madian Malfi Dec 10 '21 at 23:21
  • don't discount this suggestions. Introduced in Dart2.3 the spread operator is a valuable and concise technique for inserting multiple values into a collect, great for For Loops in Flutter UI. see docs https://dart.dev/guides/language/language-tour#spread-operator – RumbleFish Jul 12 '22 at 08:22
7

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))));
}
jvlamy
  • 326
  • 2
  • 4
6

One line

Column( children: list.map((e) => Text(e)).toList() )
reyqueson
  • 459
  • 1
  • 7
  • 9
2

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();
  }
Phuc Tran
  • 7,555
  • 1
  • 39
  • 27
2

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

Lightwaxx
  • 625
  • 1
  • 8
  • 18
1

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(...).

sjsam
  • 21,411
  • 5
  • 55
  • 102
0

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.

rawwar
  • 4,834
  • 9
  • 32
  • 57
0

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];
                        });
                      });
                })
              ],
gandharv garg
  • 1,781
  • 12
  • 17