I'm new to flutter and I'm trying to build a rather simple app.
I have this simple (and still in progress) code and I'm trying to add a new Widget every time the user clicks a button.
Here's my code:
class ResponsavelProfilePage extends StatefulWidget {
@override
_ResponsavelProfilePageState createState() => new _ResponsavelProfilePageState();
}
class _ResponsavelProfilePageState extends State<ResponsavelProfilePage> {
int _count = 1;
@override
Widget build(BuildContext context) {
List<Widget> _contatos = new List.generate(_count, (int i) => new ContactRow());
return new Scaffold(
appBar: new AppBar(
title: new Text(GlobalConstants.appName),
),
body: new LayoutBuilder(builder: (context, constraint) {
final _maxHeight = constraint.biggest.height / 3;
final _biggerFont = TextStyle(fontSize: _maxHeight / 6);
return new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: 'Nome',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new TextFormField(
decoration: new InputDecoration(
labelText: 'Data de nascimento',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new Row(
children: _contatos,
),
new FlatButton(
onPressed: _addNewContactRow,
child: new Icon(Icons.add),
),
//new ContactRow()
],
),
);
})
);
}
void _addNewContactRow() {
setState(() {
_count = _count + 1;
});
}
}
class ContactRow extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _ContactRow();
}
class _ContactRow extends State<ContactRow> {
@override
Widget build(BuildContext context) {
return new Container(
child:
new Column(
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: 'Contato',
),
),
new Text("Tipo Contato: "),
new DropdownButton(
value: _currentContactType,
items: _dropDownMenuItems,
onChanged: changedDropDownItem,
),
new Container(
padding: new EdgeInsets.all(20.0),
),
]
)
);
}
List _contactTypes =
["Phone (SMS)", "Phone (Whatsapp)", "Email"];
List<DropdownMenuItem<String>> _dropDownMenuItems;
String _currentContactType;
@override
void initState() {
_dropDownMenuItems = getDropDownMenuItems();
_currentContactType = null;
super.initState();
}
List<DropdownMenuItem<String>> getDropDownMenuItems() {
List<DropdownMenuItem<String>> items = new List();
for (String city in _contactTypes) {
items.add(new DropdownMenuItem(
value: city,
child: new Text(city)
));
}
return items;
}
void changedDropDownItem(String selectedCity) {
setState(() {
_currentContactType = selectedCity;
});
}
}
I want to add another ContactRow when the user clicks that Flat Button with addNewContactRow() on onPressed.
I've found the code on addNewContactRow() and the List.generate part on another question here, but I can't make it work.
Can some one please help?
Just replace Row with ListView.
Also made few changes in Height/width check it out.
The Result :
The Code :
import 'package:flutter/material.dart';
void main() => runApp(
new MaterialApp(
home: new ResponsavelProfilePage(),
),
);
class ResponsavelProfilePage extends StatefulWidget {
@override
_ResponsavelProfilePageState createState() =>
new _ResponsavelProfilePageState();
}
class _ResponsavelProfilePageState extends State<ResponsavelProfilePage> {
int _count = 1;
@override
Widget build(BuildContext context) {
List<Widget> _contatos =
new List.generate(_count, (int i) => new ContactRow());
return new Scaffold(
appBar: new AppBar(
title: new Text("NonstopIO"),
),
body: new LayoutBuilder(builder: (context, constraint) {
final _maxHeight = constraint.biggest.height / 3;
final _biggerFont = TextStyle(fontSize: _maxHeight / 6);
return new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: 'Nome',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new TextFormField(
decoration: new InputDecoration(
labelText: 'Data de nascimento',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new Container(
height: 200.0,
child: new ListView(
children: _contatos,
scrollDirection: Axis.horizontal,
),
),
new FlatButton(
onPressed: _addNewContactRow,
child: new Icon(Icons.add),
),
//new ContactRow()
],
),
);
}));
}
void _addNewContactRow() {
setState(() {
_count = _count + 1;
});
}
}
class ContactRow extends StatefulWidget {
@override
State<StatefulWidget> createState() => new _ContactRow();
}
class _ContactRow extends State<ContactRow> {
@override
Widget build(BuildContext context) {
return new Container(
width: 170.0,
padding: new EdgeInsets.all(5.0),
child: new Column(children: <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: 'Contato',
),
),
new Text("Tipo Contato: "),
new DropdownButton(
value: _currentContactType,
items: _dropDownMenuItems,
onChanged: changedDropDownItem,
),
new Container(
padding: new EdgeInsets.all(20.0),
),
]));
}
List _contactTypes = ["Phone (SMS)", "Phone (Whatsapp)", "Email"];
List<DropdownMenuItem<String>> _dropDownMenuItems;
String _currentContactType;
@override
void initState() {
_dropDownMenuItems = getDropDownMenuItems();
_currentContactType = null;
super.initState();
}
List<DropdownMenuItem<String>> getDropDownMenuItems() {
List<DropdownMenuItem<String>> items = new List();
for (String city in _contactTypes) {
items.add(new DropdownMenuItem(value: city, child: new Text(city)));
}
return items;
}
void changedDropDownItem(String selectedCity) {
setState(() {
_currentContactType = selectedCity;
});
}
}
This is one way to do it
First, let's see what is children
property of a column.
List<Widget> children: const <Widget> []
It is a List
of Widgets. Since Flutter UI is built in code, we can pass an array of children that we constructed before. No need to declare it in []
every time, and this comes handy when you want dynamic children inside Column
or Row
In your builder, you return Center
widget that wraps the column. since builder is a function, we can do some magic prior to returning our Widget.
Let us first extract the array from the Column itself in one variable:
List<Widget> extractedChildren = <Widget>[
new TextFormField(
decoration: new InputDecoration(
labelText: 'Nome',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new TextFormField(
decoration: new InputDecoration(
labelText: 'Data de nascimento',
),
),
new Container(
padding: new EdgeInsets.all(20.0),
),
new Row(
children: _contatos,
),
new FlatButton(
onPressed: _addNewContactRow,
child: new Icon(Icons.add),
),
//new ContactRow()
];
Now, this was the column that you had so far. What you want to do right now, looking at your question, is to add one Contact row for each _count
So we do simple old school iteration, and for each iteration we add one ContactRow
to our List
for (var i = 0; i < _count; ++i) {
extractedChildren.add(ContactRow());
}
After we have our List constructed, pass it as an argument to the children.
return new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: extractedChildren,
),
);
This is just a simple example. Remember All UI is built with pure code and you can apply all your knowledge from code building to it. For example,
we can extract the method to build the children of Column to make it more pretty
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: createChildren(),
),
Now, instead of creating our List inside the builder method, we would create new method that would return the List iself
List<Widget> createChildren(){
//same code for creating the list
}
Important thing not to forget, but you already have that in your code, is to update the _count
property inside setState
so that children get rebuilt with new _count