Home > Enterprise >  Created 2 text fields with a loop. Now they have same value
Created 2 text fields with a loop. Now they have same value

Time:06-13

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.

You can see the picture of the problem here

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(). enter image description here

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();
    }
  }
}
  • Related