Flutter for loop to generate list of widgets - flutter

I have code like this but I want it to iterate over an integer array to display a dynamic amount of children:
return Container(
child: Column(
children: <Widget>[
Center(
child: Text(text[0].toString(),
textAlign: TextAlign.center),
),
Center(
child: Text(text[1].toString(),
textAlign: TextAlign.center),
),
],
),
)
Where the text variable is a list of integers converter to string here. I tried adding a function to iterate through the array and display the 'children' but was getting a type error. Not sure how to do it since I'm new to Dart and Flutter.

You can try this :
#override
Widget build(BuildContext context) {
List<int> text = [1,2,3,4];
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: Column(
children: [
for ( var i in text ) Text(i.toString())
],
),
),
);
Note that this was added with the updated of dart to version 2.3. You can read about some of the best changes in this article
Another method that was provided before dart 2.3 is this:
#override
Widget build(BuildContext context) {
List<int> text = [1,2,3,4];
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: Column(
children: List.generate(text.length,(index){
return Text(text[index].toString());
}),
),
),
);

You can try .map Method here,
class Example extends StatelessWidget {
List <int> exampleList = [1,2,3,4];
#override
Widget build(BuildContext context) {
return
Container(
child: Column(
children: exampleList.map((i) => new Text(i.toString())).toList()
),
);
}
}
This method will come in handy if you have objects inside your list. Also with the .map() method .toList() must be used at the end.

Assuming you want to loop some widgets (e.g Text()) in the Column widget, you can add a loop inside the children property. See a sample below:
Column(
children: <Widget>[
for (int i=0; i<3; i++)
Text("Hello" + i)
],
)

You could use the map method of your list of Strings.
Widget _getListWidgets(List<String> yourList){
return Row(children: yourList.map((i) => Text(i)).toList());
}
When your List have a complex Object:
Widget _getListWidgets( List<YourObject> lstItens) {
return Row(children: lstItens.map((i) => Text(i.name)).toList());
}

The best way to do this is to make use of the List.map()
That way you do not have to enable 'control-flow-collections'
Container(
child: Column(
children: myList.map((e) => new Text(e)).toList(),
),
);

Related

Vertical viewport was given unbounded height flutter/dart

this is my code:
#override
Widget build(BuildContext context) {
return ListView(children: [
Card(
child: Row(
children: [
ListTile(
title: Text(publicacion.title),
subtitle: Text(publicacion.body),
)
],
)),
]);
}
Surely it is some overflow, but I couldn't fix it, I can't find the way .. I tried to implement ListView.builder, but I wouldn't be knowing where to fit it
Try to add your Inside Row widgets wrap it with Expanded or Flexible refer my answer here or here or here hope its helpful to you
Refer Expanded here
Try below code hope its helpful to you.
ListView(
shrinkWrap: true,
children: [
Card(
child: Row(
children: [
Expanded(
child: ListTile(
title: Text(
'publicacion.titlepublicacion.titlepublicacion.titlepublicacion.titlepublicacion.titlepublicacion.title'),
subtitle: Text(
'publicacion.bodypublicacion.titlepublicacion.titlepublicacion.titlepublicacion.titlepublicacion.titlepublicacion.title'),
),
)
],
),
),
],
),
Your Result Screen->
The Row widget is creating this problem, just remove Row Widget and it should work fine.
#override
Widget build(BuildContext context) {
return ListView(children: [
Card(
child: ListTile(
title: Text(publicacion.title),
subtitle: Text(publicacion.body),
)),
]);
}

How to show text in in the body of flutter?

I wanted to show text from a string variable before the Wrap widget. But when I wanted to add something like Text before Wrap widget it is giving me error. How can show some text before the Wrap widget.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Second Route"),
),
body: Center(
child:
Wrap(
children: _buildButtonsWithNames(),
),
)
);
}
You can give column as a child to the center widget like this:
body: Center(
child: Column(
children: [
Text("Your Text"),
Wrap(
children: _buildButtonsWithNames(),
),
],
),
),
Hope it answers the question.
If you want the text to be on top of the Wrap, add your Wrap to a Column and before of it add your Text widget.
If you want the text to be just before the Wrap, add your Wrap to a Row widget and do the same as for Column.
Just found a way right now!!! from another answer.
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Second Route"),
),
body: Column(
children: <Widget>[
Text("He"),
Expanded(
child: Center(
child: Wrap(
children: _buildButtonsWithNames(),
),
),
)
],
),
);
}
}
Just use the column widget and pass the text and wrap widgets as children of the column

Card inside a card

Creating an app in Flutter for a local resturant/pub/bistro and I want to display a card above all other cards in the menu to show temporary messages/deals. My implementation relies on a menu_screen and a "handler" of sorts (not fully developed mind you).
But there's two problems
It displays as a card within a card
the information is displayed twice when it should only be shown once at the top of the screen
Here's what it looks like when run on a device for testing:
Pastebin for menu_screen: enter link description here
// sets up the menu screen for our program
// imports
import 'package:flutter/material.dart';
import 'package:elehouseapp/handlers/food_menu_handler.dart';
// set up class
class Menus extends StatelessWidget{
#override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: TabBar(
labelColor: Colors.black,
indicatorColor: Colors.black,
tabs: <Widget>[
Tab(
text: 'Food',
),
Tab(
text: 'Drink',
),
]
),
body: TabBarView(
children: <Widget>[
FoodHandler(),
//Text("Food"),
Text("Drink"),
],
),
),
);
}
}
Pastebin for food_menu_handler: enter link description here
import 'package:flutter/material.dart';
// Generates an 2D array of food items and places elements into cards
// Some data
final items = ['test','test2'];
final desc = ["Loreum Isplum","Other Test"];
final price = [2.20,20.00];
// Handles array data and puts into cards
class FoodHandler extends StatelessWidget{
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index){
return Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Visibility(
visible: true,
child: Card(
child: ListTile(
title: Text("COVID-19"),
subtitle: Text("Menu restrictions are in place"),
),
)
),
ListTile(
title: Text(items[index]),
subtitle: Text(desc[index]+"\n£"+price[index].toString()),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: Text("Add to Basket"),
onPressed: null,
),
],
),
],
),
);
}
);
}
}
EDIT I've un-nested the card (as pointed out). Hopefully this may explain what I have with explanations of what I'm trying to do
You could change the ListView.builder in a normal ListView and generate the list of its children using conditions and loops inside the list as follow:
// Generates an 2D array of food items and places elements into cards
// Some data
final items = ['test', 'test2'];
final desc = ["Loreum Isplum", "Other Test"];
final price = [2.20, 20.00];
// Handles array data and puts into cards
class FoodHandler extends StatelessWidget {
#override
Widget build(BuildContext context) {
return ListView(
children: [
if (1 == 1) // TODO your condition here
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Card(
child: ListTile(
title: Text("COVID-19"),
subtitle: Text("Menu restrictions are in place"),
),
),
],
),
),
for (int index = 0; index < items.length; index++)
Card(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListTile(
title: Text(items[index]),
subtitle: Text(desc[index] + "\n£" + price[index].toString()),
),
ButtonBar(
children: <Widget>[
FlatButton(
child: Text("Add to Basket"),
onPressed: null,
),
],
),
],
),
),
],
);
}
}
I marked with a TODO the line where you have to insert the condition for the first "alert".
Also I suggest you to use a class to represent the list items instead of having three different lists and "joining" them with the index, it's a more convenient and elegant solution

How to wrap column items in a card with an header with Flutter

I'm new to Flutter/Dart, so maybe the problem I'have is just lack of knowledge. My goal is to build a card with an horizontal header on top of the card and then the card should display a list of item/value pairs vertically, wrapping them to a new column if the device is large enough. I've added a Column, for two children (the header and the Wrap), but if it's embedded in a column there's no wrapping at all.
I tried a lot of combinations but I didn't find a solution. If I remove the column, the Wrap widget works perfectly.
class TestApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return
MaterialApp(
title: 'Wrap Test',
theme: ThemeData(
primarySwatch: Colors.red,
),
home: TestScreen(),
);
}
}
class TestScreen extends StatelessWidget {
/*
Builds a single item/value pair
*/
Widget _text(int i) {
var container = Container(
height: 50,
child: Row(
children: <Widget>[
Container(
width: 200,
child: Text(
'item $i',
),
),
Text(
'value $i',
),
],
),
);
return container;
}
/*
Builds a list of item/value pairs
*/
List<Widget> _items(int n) {
List<Widget> widgetList = [];
for (int i = 1; i <= n; i++) {
widgetList.add(_text(i));
}
return widgetList;
}
/*
This way Wrap widget isn't working as I thought...the reason is that it seems bounded by
the column and the column does not expands itself due to wrapping
*/
Widget buildWrapNotWorking(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Wrap Test"),
),
body: Card(
color: Colors.yellow,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Header",
),
Wrap(
direction: Axis.vertical,
runSpacing: 50,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,
children: _items(20),
),
],
),
),
);
}
/*
This way Wrap widget is working, because I removed the column. But I need to have a card header
on top of the card.
*/
Widget buildWrapWorkingButNoHeader(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Wrap Test"),
),
body: Card(
color: Colors.yellow,
child: Wrap(
direction: Axis.vertical,
runSpacing: 100,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,
children: _items(20),
),
),
);
}
#override
Widget build(BuildContext context) {
return buildWrapNotWorking(context);
// return buildWrapWorkingButNoHeader(context);
}
}
I'm expecting that calling buildWrapNotWorking(context) will work as desired.
The problem is similar to that one:
How to wrap row items in a card with flutter
Simply wrap you Wrap widget with - Expanded - this was it will get enough space in column.
code:
Widget buildWrapNotWorking(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Wrap Test"),
),
body: Card(
color: Colors.yellow,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Header",
),
Expanded(
child: Wrap(
direction: Axis.vertical,
runSpacing: 50,
crossAxisAlignment: WrapCrossAlignment.start,
spacing: 20,
children: _items(20),
),
),
],
),
),
);
}

Determine Scroll Widget height

I would like to determine if a 'scrollable' widget indeed needs to scroll. I would ultimately like to show something like 'Scroll for more'. How do I achieve this?
I could use a combination of LayoutBuilder and a ScrollController. However, ScrollController gives me maxScrollExtent and minScrollExtent only after any event - like say for example user trying to scroll
I would like to know during 'build' so that I can determine to show 'Scroll for more' or not depending on the screen dimensions
class _MyHomePageState extends State<MyHomePage> {
int value = 0;
String text = 'You have pushed the button 0 times\n';
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
child: Text(text),
),
),
Expanded(
flex: 1,
child: Text('Scroll for more')),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
value += 1;
text = text + 'You have pushed the button $value times \n';
});
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
I would like to dynamically display
Text('Scroll for more'),
only if the SingleChildScrollView needs to be scrolled to view the entire content. Please note, I am just giving the above code as example.
In my real case, I have a StreamBuilder inside a SingleChildScrollView and I cannot determine how much of data is going to be flowing in from the stream. I also do not have any button or tap or any gesture to access the controller and setState
Thanks in advance for any help
class _MyHomePageState extends State<MyHomePage> {
bool showMore = false;
final scrollController = ScrollController();
#override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback((_) {
setState(() {
showMore = scrollController.position.maxScrollExtent > 0;
});
});
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
flex: 9,
child: SingleChildScrollView(
controller: scrollController,
child: SizedBox(height: 650, child: Text('blah')),
),
),
if (showMore) Expanded(flex: 1, child: Text('Scroll for more')),
],
),
),
);
}
}