163

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.

1
  • 1
    Why don't you use ListView? Can you share more code of how you are doing? Commented May 21, 2018 at 3:54

13 Answers 13

240

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());
  }
Sign up to request clarification or add additional context in comments.

6 Comments

is it also possible to return an array of Widgets?
Since dart 2.3, you can use the new for loop method: return Row(children: [ for (var name in list) Text(name) ])
can you get an index value using this method?
@LeeCasey just use a for (var i = 0; i<list.length;i++) loop, works perfectly fine aswell
@smac89 - how can you add an if condition to that?
|
157

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.

9 Comments

Hi, I might be a little late, But I need to know that what should I do If I want multiple widgets to show in a loop?
You can wrap them in a column or any widget that can hold multiple widgets. If I got your question clearly.
How can I put more than one element in the loop? E.g. for(var item in list ) { Text(item),Spacer() } wouldn't work.
U will need to wrap the two widgets in another widget.
is there anyway to get an index value or "count" from this method? I am looping through an array of widgets which contain a button and I want to know which button is pressed.
|
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".

4 Comments

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
how could I get the index inside that .map?
@dani you can't with map, but you can with the for loop in rizky's answer
list.map((name) => new Text(name)).toList(); I forgot to add toList() function at the end. Thanks bro :)
51

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(),
    );
  }
}

Comments

25

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

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

4 Comments

Thats useful if you need index to check selected item for example.
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 dart.dev/guides/language/language-tour#spread-operator
I wish I could upvote this more than once. Great suggestion! So simple and intuitive.
I like this suggestion as I would imagine the compiler could optimize that into one loop and one array rather than flattening at the end
10

One line

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

Comments

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))));
}

Comments

3

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();
  }

2 Comments

List<Text> is not subtype of Widget bro
It still works on my side. Did you get error? (I updated my code to add 1 more method)
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

Comments

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

Comments

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.

Comments

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];
                        });
                      });
                })
              ],

Comments

0

I wanted to build on the answer from ltk and provide an example. This was a simple app that I created as part of a Udemy course where you create a Xylophone app:

import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const App());
}

class App extends StatelessWidget {
  const App({super.key});

  // The default data type for a List of Objects is <Map<String, Object>>.
  // However, it is important to specify <Map<String, dynamic>> as the data 
  // type otherwise you will get errors when you try to reference the Object
  // properties inside the Expanded widget.
  // See https://stackoverflow.com/a/67231333/9453009
  static final soundButtons = <Map<String, dynamic>>[
    {'color': Colors.red, 'number': 1},
    {'color': Colors.orange, 'number': 2},
    {'color': Colors.yellow, 'number': 3},
    {'color': Colors.green, 'number': 4},
    {'color': Colors.teal, 'number': 5},
    {'color': Colors.blue, 'number': 6},
    {'color': Colors.purple, 'number': 7},
  ];

  void playSound(int soundNumber) async {
    final player = AudioPlayer();
    await player.play(AssetSource('note$soundNumber.wave'));
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        backgroundColor: Colors.black,
        body: SafeArea(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              for (int i = 0; i < soundButtons.length; i++) ...[
                Expanded(
                  child: TextButton(
                    onPressed: () {
                      playSound(soundButtons[i]['number']);
                    },
                    style: TextButton.styleFrom(
                      backgroundColor: soundButtons[i]['color'],
                    ),
                    child: const Text(''),
                  ),
                ),
              ],
            ],
          ),
        ),
      ),
      debugShowCheckedModeBanner: false,
    );
  }
}

NOTE: I could not get the sound files to work due to some build issues with Linux, but I think this is how you would use the audioplayers package.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.