I am really confused with this, where should the FOR LOOP be placed, so that I don't get an error in flutter? As you can see on the picture, it has red underlines and it says.
Two alternatives :
final children = <Widget>[];
for (var i = 0; i < 10; i++) {
children.add(new ListTile());
}
return new ListView(
children: children,
);
or
return new ListView(
children: new List.generate(10, (index) => new ListTile()),
);
There are multiple ways of using a for loop in children for widgets like ListView, Column, etc.
Using a for loop
ListView(
children: [
for (var i = 0; i < 10; i++) Text('Item $i'),
],
)
Using a for-each loop
ListView(
children: [
for (var item in items) Text(item),
],
)
Combining ...[] with for loop
ListView(
children: [
...[
Text('Item 0'),
Text('Item 1'),
],
for (var item in items) Text(item), // Rest of the items
],
)
We can use the spread operator also to add multiple widgets using for the loop
Column(
children: [
Container() /// we can add some other widgets
for (var i = 0; i < 3; i++) ...[
CardListItem(),
Divider(),
],
]
Simple for loop in flutter using json response
Widget build(BuildContext context) {
var list = [{'id':"123123","date":"20/08/2016"},{'id':"123124","date":"26/08/2016"},{'id':"123125","date":"26/08/2016"}];
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('Recent Claims'),
Table(
border: TableBorder.all(color: Colors.black),
columnWidths: {
0: FixedColumnWidth(100.0),
1: FixedColumnWidth(100.0)
},
children:[
for(var item in list ) TableRow(children: [
Text(item['id']),
Text(item['date']),
])]
),
}
If you don't have a List:
ListView(
scrollDirection: Axis.horizontal,
children: List.generate(10, (index) => …),
),
Else:
ListView(
scrollDirection: Axis.horizontal,
children: list.map((e) => …).toList(),
),
Related
I entered the data from json, everything works fine, but now only the first element is displayed. How can I write here like foreach so that all elements with a loop are displayed.
You can see the code here
You should wrap the data with listview builder.
Try code something like this:
...
else if(snapshot.hasData ){
return ListView.builder (
itemCount: snapshot.data.length, \\length of snapshot data
itemBuilder: (BuildContext ctxt, int index) {
return Container(
child:Column(
children: <Widget>[
new Padding(padding: const EdgeInsets.all(5.0)),
new Container(
child: new Text(
'Тип штрафа: ${data[index]['VDescription']}'
),
),
new Container(
child: new Text(
'Адрес: ${data[index]['VLocation']}'
),
),
new Container(
child: new Text(
'Дата: ${data[index]['VTime']}'
),
),
],
),
);
}
);
I hope this will work. :)
I'm making a new stateful widget that would show a listview according to the option selected, which are ONE and TWO here. The value of index changes once the GestureDetector is tapped, fontsize and color of the text changes. but, the Container with pages[index] does not rebuild
I don't know what is wrong since, one of the container in the column rebuilds and the other doesn't.
#override
State<StatefulWidget> createState() {
// TODO: implement createState
return MatchStatsState();
}
}
class MatchStatsState extends State<MatchStats>{
List<Widget> pages = [
ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
BattingStatsView(CskBatting),
BowlingStatsView(cskBowling),
],
),
ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
BattingStatsView(kxipBatting),
BowlingStatsView(kxipBowling)
],
),
];
Color activeColor = Colors.yellow;
Color inactiveColor = Colors.white;
num activeFontSize = 20.0;
num inactiveFontSize = 15.0;
int index = 0;
#override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
height: MediaQuery.of(context).size.height*0.4,
width: MediaQuery.of(context).size.width*0.95,
child: Column(
children: <Widget>[
Container(
height: MediaQuery.of(context).size.height*0.05,
width: MediaQuery.of(context).size.width*0.95,
child: Row(
children: <Widget>[
GestureDetector(
onTap: (){
setState(() {
index = 0;
});
},
child: Container(
width: MediaQuery.of(context).size.width*0.45,
child: Text("ONE",style: TextStyle(color: index == 0?activeColor:inactiveColor,fontSize: index == 0? activeFontSize: inactiveFontSize)),
),
),
GestureDetector(
onTap: (){
setState(() {
index = 1;
});
},
child: Container(
width: MediaQuery.of(context).size.width*0.45,
child: Text("TWO",style: TextStyle(color: index == 1?activeColor:inactiveColor, fontSize: index == 1? activeFontSize: inactiveFontSize)),
),
),
],
),
),
Container(
height: MediaQuery.of(context).size.height*0.35,
width: MediaQuery.of(context).size.width*0.95,
child: pages[index]
),
]
)
);
}
}
I want the second container in the column to rebuild when the value of index changes, how could I achieve that?
Try with this:
create a method that return a List Widget like this:
List<Widget> buildPages() {
return [
ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
BattingStatsView(CskBatting),
BowlingStatsView(cskBowling),
],
),
ListView(
scrollDirection: Axis.horizontal,
children: <Widget>[
BattingStatsView(kxipBatting),
BowlingStatsView(kxipBowling)
],
),
];
}
Widget getProperWidget(int index) {
return buildPages()[index];
}
Than your column container:
Container(
height: MediaQuery.of(context).size.height*0.35,
width: MediaQuery.of(context).size.width*0.95,
child: getproperWidget(index)
),
Remember to override the initState.
I think the cause of this issue is the element tree doesn't recognize the change that has been done in the widget tree , so you can add Key to the container which holds pages[index] or
you can do something like this :
Widget getWidget(int index){
return Container(
child:pages[index],
);
}
instead of using Container in the widget tree, use a function that will be called every time the ui re renders .
I hope that can help
am using my customized ExpandedList .. but the header is overflowed when the text is long ....
and this is how am using it:
new CustomExpansionPanelList2(
expansionCallback: (int index, bool isExpanded) {
setState(() {
faqList[index].isExpanded =
!faqList[index].isExpanded;
});
},
children: faqList.map((Faq i) {
return new ExpansionPanel(
headerBuilder:
(BuildContext context, bool isExpanded) {
return new Container(
child: new Row(
children: <Widget>[
SizedBox(child: Text(
i.question,
style:
TextStyle(color: Color(0xff606060)),
),),
],
),
);
},
isExpanded: i.isExpanded,
body: new Center(
child: new Padding(
padding: EdgeInsets.all(10.0),
child: Text(i.answer),
),
),
);
}).toList(),
),
I want the text to be in two lines or more is needed to make it fit the width of the Expanded list..
How to achieve this?
Just replace SizedBox by Expanded in the headerBuilder
return new Container(
child: new Row(
children: <Widget>[
Expanded(child: Text(
i.question,
style:
TextStyle(color: Color(0xff606060)),
),),
],
),
);
I'm trying to create a word game. I have a local json file where I'm retrieving data from. I'm able to retrieve the data and display it on the first row. What I'm trying to do is on tap of one block (on the first row), get the value and display it in order on the second row.
I'm able to retrieve the value but I can't display it on the second row. I tested this by printing the value in the console.
Updated code:
body: new Container(
child: new Center(
// Use future builder and DefaultAssetBundle to load the local JSON file
child: new FutureBuilder(
future: DefaultAssetBundle
.of(context)
.loadString('data_repo/starwars_data.json'),
builder: (context, snapshot) {
var newData = JSON.decode(snapshot.data.toString());
List<Widget> listMyWidgets() {
List<Widget> list = new List();
for (var i = 0; i < newData.length; i++) {
var word = newData[i]['word']["letters"];
for (var n = 0; n < word.length; n++) {
list.add(new Text(word[n]['letter']));
}
}
return list;
}
List letters = [];
for (int i = 0; i < listMyWidgets().length; i++) {
var listMyWidgetsToString =
listMyWidgets()[i].toString();
var listWidgetToList =
listMyWidgetsToString.replaceAll('Text("', "");
var completeWordList =
listWidgetToList.replaceAll('")', "");
letters.add(completeWordList);
}
return new ListView.builder(
itemBuilder: (BuildContext context, int index) {
return new Card(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
new Column(children: [
new Image.asset(newData[0]['image'])
]),
new GridView.count(
shrinkWrap: true,
crossAxisCount: listMyWidgets().length,
children: new List.generate(
listMyWidgets().length,
(i) => new GestureDetector(
onTap: () {
final int wordLength =
5; //this is a ref to the lenght of the word so you do not keep adding tiles
setState(() {
(letters.length + 1) <=
wordLength * 2
? letters.add(letters[i])
: null;
});
},
child: new Card(
elevation: 5.0,
color: Colors.brown[500],
child: new Padding(
padding:
const EdgeInsets.all(8.0),
child: new Center(
child:
new Text(letters[i])),
),
),
)),
),
],
),
);
},
itemCount: newData == null ? 0 : newData.length,
);
}),
),
)
It depends on how you want to structure your data. In this example, I just add the pressed letters into the same array for the word and it will do the job.
Note that I keep a reference (which you may add to your JSON) which is the initial length of the word so it stops adding tiles when all letters are used.
Also you need to have a StatefulWidget in order for this to work
Probably there is a better a way to handle this but that is what I managed to do atm.
class GridViewWords extends StatefulWidget {
#override
GridViewWordsState createState() {
return new GridViewWordsState();
}
}
class GridViewWordsState extends State<GridViewWords> {
List letters = [
"A",
"M",
"C",
"I",
"C"
];
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new GridView.count(
shrinkWrap: true,
crossAxisCount: 5,
children: new List.generate(letters.length, (i)=>
new GestureDetector(
onTap: (){
final int wordLength =5; //this is a ref to the lenght of the word so you do not keep adding tiles
setState((){
(letters.length+1)<=wordLength*2? letters.add(letters[i]):null;
});
},
child: new Card(
elevation: 5.0,
color: Colors.brown[500],
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new Center(child: new Text(letters[i])),
),
),
)),
),
);
}
}
I'm basically creating a game where a grid is getting generated dynamically. It creates the tiles, adds them to a list and uses that list as an argument for the children parameter. What I find difficult however is combining it with fixed widgets.
Let's say on top of everything, I want a text element. The problem I now encounter is that if I assign my dynamically generated content like this:
...
children: mycontent,
...
then I have nowhere to put my hard coded widgets. I hope you know what I mean. Until now, I have solved it by creating a larget list and copying the dynamically generated elements over, and afterwards adding my hard-coded widgets:
Widget buildTile(int counter) {
return new GestureDetector(
onTap: (){
setState((){
toggleColor(counter);
});
},
child: new Container(
color: colors[counter],
foregroundDecoration: new BoxDecoration(
border: new Border(),
),
width: 75.0,
height: 75.0,
margin: new EdgeInsets.all(2.0),
)
);
}
List<Widget> buildGrid(){
Map dimensions = {"width" : 4, "height" : 6};
List<Widget> grid = new List<Widget>(dimensions["height"]);
List<Widget> tiles = [];
int counter = 0;
for (int i = 0; i < dimensions["height"]; i++){
tiles = [];
for (int j = 0; j < dimensions["width"]; j++){
tiles.add(buildTile(counter));
counter++;
}
grid[i] = new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: tiles,
);
}
return grid;
}
List<Widget> copyElements(List<Widget> from){
List<Widget> to = [];
for (int i = 0; i < from.length; i++){
to.add(from[i]);
}
return to;
}
List<Widget> buildPlayground(List<Widget> grid){
List<Widget> playground = [];
playground = copyElements(grid);
playground.add(new Padding(
padding: new EdgeInsets.all(20.0),
child: new RaisedButton(
color: Colors.purple,
child: new Container(
width: 100.0,
child: new Center(
child: new Text("Done", style: new TextStyle(fontSize: 20.0)),
),
),
onPressed: (){
}
),
));
return playground;
}
#override
build(BuildContext context){
return new Scaffold(
appBar: new AppBar(
title: new Text("Game"),
),
body: new Container(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: buildPlayground(buildGrid()),
)
),
);
}
It kinda works, but is very tedious as soon as I figure out that I want to add another hard coded widget. Any suggestions for how I can address this problem?
Thanks
I guess this is the way to go, however you could use the GridView widget, and you could create a TileWidget instead of you buildTile function. Using GridView should clean your code, but what do you mean by hard-coded widgets ?
You can combine the accepted answer from this question and use the spread operator, like what's below, to combine a list with another list or with singular items.
List<Widget> generatedWidgets = generateWidgetList();
List<Widget> hardCodedWidgets = hardCodeWidgetList();
Widget singleHardCodedWidget = Container(
child: Text('some text'),
);
combinedList = [...generatedWidgets, ...hardCodedWidgets, singleHardCodedWidget];