I am making an app where a user can add multiple stateful widgets in a list view inside a home stateful widget. Example: list of ingredients. Every ingredient widget has a TextFormField. A user can add as many ingredients as wish to and eventually the data entered will be updated using a POST request from the home stateful widget.
Question: How do i access the TextFormField controller of every added ingredient widget from the home state. Also is this the best way to approcah this or is there a better way?
class home extends StatefulWidget {
const home({Key? key}) : super(key: key);
@override
_homeState createState() => _homeState();
}
class _homeState extends State<home> {
List<Widget> listRecipe = [];
addIngredient(){
listRecipe.add(new Ingredient());
}
Future<http.Response> postData(){
return http.post(
Uri.parse(recipeData),
headers: <String, String>{
'Authorization':'token $token',
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
"name":"something",
"ingredients":'{"ingredient1": "value", "ingredient2": "value", ...... }'
}),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Container(
child: Wrap(
children:[ ListView(
children:listRecipe,
),
],
),
),
] ),
bottomNavigationBar: Row(
children:
[FloatingActionButton(
onPressed: addIngredient,
),
FloatingActionButton(onPressed: postData)
]));
}
}
class Ingredient extends StatefulWidget {
const Ingredient({Key? key}) : super(key: key);
@override
_IngredientState createState() => _IngredientState();
}
class _IngredientState extends State<Ingredient> {
final _typeController = TextEditingController();
@override
Widget build(BuildContext context) {
return Container(
child: Padding(
padding: EdgeInsets.only(left: 22, top: 20, right: 15),
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: Text(
"Process",
style: TextStyle(
fontFamily: 'Nunito',
color: Colors.grey.shade500,
fontSize: 12,
),
textAlign: TextAlign.left,
),
),
),
Container(
height: 45,
child: TextFormField(
keyboardType: TextInputType.text,
maxLines: null,
controller: _typeController,
style: TextStyle(fontSize: 15, fontFamily: 'Nunito'),
decoration: InputDecoration(
hintText: 'Enter Process',
hintStyle: TextStyle(color: Colors.grey.shade400),
contentPadding: EdgeInsets.only(left: 5),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.green)),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.green, width: 2),
),
),
),
),
],
),
),
);;
}
}
tl:dr
textEditingController.text = widget.stringValue
textEditingController.text = widget.stringValue
I would suggest to add a model for your Ingredient, e.g:
class Ingredient {
String? data;
Ingredient({this.data});
}
Rename the Ingredient Widget to e.g. IngredientWidget
. The type of your listReceipe
should stay List<Ingredient>
:
List<Ingredient> listRecipe = [];
addIngredient() {
setState(() {
listRecipe.add(Ingredient());
});
}
Change the body of your Home Widget:
body: Container(
child: ListView.builder(
itemCount: listRecipe.length,
itemBuilder: (context, index) =>
IngredientWidget(listRecipe[index]))),
Now change your IngredientWidget like this:
class IngredientWidget extends StatefulWidget {
const IngredientWidget(this.ingredient, {Key? key}) : super(key: key);
final Ingredient ingredient;
@override
_IngredientWidgetState createState() => _IngredientWidgetState();
}
class _IngredientWidgetState extends State<IngredientWidget> {
final _typeController = TextEditingController();
@override
void didUpdateWidget(covariant IngredientWidget oldWidget) {
super.didUpdateWidget(oldWidget);
_typeController.text = widget.ingredient.data ?? '';
}
@override
void initState() {
super.initState();
_typeController.text = widget.ingredient.data ?? '';
}
@override
Widget build(BuildContext context) {
// stays the same
}
}
You can pass a Value to your Ingredient widget:
Ingredient(data: 'Hello World')
So you can also update an Ingredient in listReceipe with
setState(() {
listRecipe[index] = Ingredient(data: anyStringValue);
});
and your list should update accordingly.