Im trying to create a trainings app for myself. I have created a app that can add more textfields everytime a button is pushed. The problem im having is all the fields have the same value and i cant enter two diffrent
I have tried a couple of things and the closest i got was when i could do diffrent values but not save it.
My code is the following:
class EventEditingPage extends StatefulWidget {
final Event? event;
const EventEditingPage({Key? key, this.event}) : super(key: key);
@override
_EventEditingPageState createState() => _EventEditingPageState();
}
class _EventEditingPageState extends State<EventEditingPage> {
final _formKey = GlobalKey<FormState>();
final setsController = TextEditingController();
final repsController = TextEditingController();
final loadController = TextEditingController();
late DropdownButton Exercise;
@override
void initState() {
super.initState();
final event = widget.event!;
exercise = event.Exercise;
setsController.text = event.Sets;
repsController.text = event.Reps;
loadController.text = event.Load;
}
}
@override
void dispose() {
setsController.dispose();
repsController.dispose();
loadController.dispose();
super.dispose();
}
int numberOfTextFields = 1;
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
leading: CloseButton(),
actions: buildEditingActions(),
backgroundColor: Colors.red,
),
body: SingleChildScrollView(
padding: EdgeInsets.all(12),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("text", style: TextStyle(fontSize: 24)),
SizedBox(height: 12),
Text("\ntext", style: TextStyle(fontSize: 24)),
for(int i = 0 ; i < numberOfTextFields ; i )
buildExerciseComplete(),
buildAddMore(),
],
),
),
),
);
List<Widget> buildEditingActions() => [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
primary: Colors.transparent, shadowColor: Colors.transparent),
onPressed: saveForm,
icon: Icon(Icons.done),
label: Text('Save'),
)
];
Widget buildExerciseComplete() => Container(
padding: EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: Color.fromARGB(255, 255, 200, 200),
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
width: 2.0,
),
),
height: 50,
child: Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(child: buildExercise()),
Expanded(child: buildSets()),
Expanded(child: buildReps()),
Expanded(child: buildLoad()),
],
),
),
],
),
);
Widget buildSets() => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: TextStyle(fontSize: 15),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Sets',
),
onFieldSubmitted: (_) => saveForm,
validator: (sets) =>
sets != null && sets.isEmpty ? 'Sets cannot be empty' : null,
controller: setsController,
);
Widget buildReps() => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: TextStyle(fontSize: 15),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Reps',
),
onFieldSubmitted: (_) => saveForm,
validator: (reps) =>
reps != null && reps.isEmpty ? 'Reps cannot be empty' : null,
controller: repsController,
);
Widget buildLoad() => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: TextStyle(fontSize: 15),
decoration: InputDecoration(
labelText: 'Load',
border: OutlineInputBorder(),
),
onFieldSubmitted: (_) => saveForm,
validator: (load) =>
load != null && load.isEmpty ? 'Load cannot be empty' : null,
controller: loadController,
);
String exercise = 'Squat';
Widget buildExercise() => DropdownButton<String>(
value: exercise,
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.black),
onChanged: (String? newValue) {
setState(() {
exercise = newValue!;
});
},
items: <String>['Squat', 'Deadlift', 'Bench']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
Widget buildAddMore() => ElevatedButton(
onPressed: () {
setState((){
numberOfTextFields ;
_textEditingControllers.add(TextEditingController());
});
},
child: Text('Add new exercise'),
);
Future saveForm() async {
final isValid = _formKey.currentState!.validate();
if (isValid) {
final event = Event(
Exercise: exercise,
Sets: setsController.text,
Reps: repsController.text,
Load: loadController.text);
final isEditing = widget.event != null;
final provider = Provider.of<EventProvider>(context, listen: false);
if (isEditing) {
provider.editEvent(event, widget.event!);
Navigator.of(context).pop();
} else {
provider.addEvent(event);
}
Navigator.of(context).pop();
}
}
}
class Event {
final Exercise;
final String Sets;
final String Reps;
final String Load;
final Color backgroundColor;
const Event({
required this.Sets,
required this.Reps,
required this.Load,
this.backgroundColor = Colors.lightGreen,
});
}
Im pretty the reason has something to do with TextEditingController()
Im hoping that somebody could help me with the solution to my problem :-)
CodePudding user response:
It seems you are building all of the fields with the same controller, so all will have the same value. You should make a list of controllers and add controllers.
Declare variables like this:
final List<TextEditingController> setsController = [];
final List<TextEditingController> repsController = [];
final List<TextEditingController> loadController = [];
Then Create Controller:
createControllers() {
for (var i = 0; i < numberOfTextFields; i ) {
setsController.add(TextEditingController());
repsController.add(TextEditingController());
loadController.add(TextEditingController());
}
}
Use the controller in respective text Field:
controller: repsController[index]
Call the create controller method at initState().
I guess you can handle the value retrieve and dispose of the controllers from here.
I don't know your save logic, so I commented it out.
class _EventEditingPageState extends State<EventEditingPage> {
final _formKey = GlobalKey<FormState>();
final List<TextEditingController> setsController = [];
final List<TextEditingController> repsController = [];
final List<TextEditingController> loadController = [];
// late DropdownButton Exercise;
List<String> exceriseItems = ['Squat', 'Deadlift', 'Bench'];
List<String> selectedExercise = [];
int numberOfTextFields = 1;
@override
void initState() {
super.initState();
final event = widget.event!;
createControllers();
selectedExercise[0] = event.Exercise != "" ? event.Exercise : exceriseItems[0];
setsController[0].text = event.Sets;
repsController[0].text = event.Reps;
loadController[0].text = event.Load;
}
@override
void dispose() {
setsController.forEach((TextEditingController element) {
element.dispose();
});
// Or simply clear the array.
// todo : repsController dispose();
// todo : loadController dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
leading: const CloseButton(),
actions: buildEditingActions(),
backgroundColor: Colors.red,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const Text("text", style: TextStyle(fontSize: 24)),
const SizedBox(height: 12),
const Text("\ntext", style: TextStyle(fontSize: 24)),
for (int i = 0; i < numberOfTextFields; i )
buildExerciseComplete(i),
buildAddMore(),
],
),
),
),
);
List<Widget> buildEditingActions() => [
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
primary: Colors.transparent, shadowColor: Colors.transparent),
onPressed: saveForm,
icon: const Icon(Icons.done),
label: const Text('Save'),
)
];
Widget buildExerciseComplete(int idx) => Container(
padding: const EdgeInsets.all(5.0),
decoration: BoxDecoration(
color: const Color.fromARGB(255, 255, 200, 200),
borderRadius: BorderRadius.circular(5.0),
border: Border.all(
width: 2.0,
),
),
height: 50,
child: Row(
children: <Widget>[
Expanded(
child: Row(
children: <Widget>[
Expanded(child: buildExercise(idx)),
Expanded(child: buildSets(idx)),
Expanded(child: buildReps(idx)),
Expanded(child: buildLoad(idx)),
],
),
),
],
),
);
Widget buildSets(int idx) => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: const TextStyle(fontSize: 15),
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Sets',
),
onFieldSubmitted: (_) => saveForm,
validator: (sets) =>
sets != null && sets.isEmpty ? 'Sets cannot be empty' : null,
controller: setsController[idx],
);
Widget buildReps(int idx) => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: const TextStyle(fontSize: 15),
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Reps',
),
onFieldSubmitted: (_) => saveForm,
validator: (reps) =>
reps != null && reps.isEmpty ? 'Reps cannot be empty' : null,
controller: repsController[idx],
);
Widget buildLoad(int idx) => TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
style: const TextStyle(fontSize: 15),
decoration: const InputDecoration(
labelText: 'Load',
border: const OutlineInputBorder(),
),
onFieldSubmitted: (_) => saveForm,
validator: (load) =>
load != null && load.isEmpty ? 'Load cannot be empty' : null,
controller: loadController[idx],
);
Widget buildExercise(int idx) => DropdownButton<String>(
value: selectedExercise[idx],
icon: const Icon(Icons.arrow_drop_down),
elevation: 16,
style: const TextStyle(color: Colors.black),
onChanged: (String? newValue) {
setState(() {
selectedExercise[idx] = newValue!;
});
},
items: exceriseItems
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
Widget buildAddMore() => ElevatedButton(
onPressed: () {
setState(() {
numberOfTextFields ;
createControllers();
});
},
child: const Text('Add new exercise'),
);
createControllers() {
for (var i = 0; i < numberOfTextFields; i ) {
setsController.add(TextEditingController());
repsController.add(TextEditingController());
loadController.add(TextEditingController());
}
selectedExercise.add(exceriseItems[0]);
}
Future saveForm() async {
final isValid = _formKey.currentState!.validate();
if (isValid) {
// final event = Event(
// Exercise: exercise,
// Sets: setsController.text,
// Reps: repsController.text,
// Load: loadController.text);
final isEditing = widget.event != null;
// final provider = Provider.of<EventProvider>(context, listen: false);
// if (isEditing) {
// provider.editEvent(event, widget.event!);
//
// Navigator.of(context).pop();
// } else {
// provider.addEvent(event);
// }
//
// Navigator.of(context).pop();
}
}
}