skip to Main Content

I am trying to create a ‘create a task’ page where the user can add or delete todo textfields for the task. I created a list of TextEditingControllers to dynamically add and remove from it for mapping them to textformfields.. I have no issue in adding but when i try to remove a specific TextEditingController (either by index or by reference) from the list, it always result in removing the last textformfield..

import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:gdsc_session2/constants/image_strings.dart';

import '../widgets/text_field.dart';

class NewTask extends StatefulWidget {
  const NewTask({super.key});

  @override
  State<NewTask> createState() => _NewTaskState();
}

class _NewTaskState extends State<NewTask> {
  final GlobalKey<FormState> formKey = GlobalKey();
  final List<TextEditingController> controllers = [];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Expanded(
            child: Form(
              key: formKey,
              child: ListView(
                children: [
                  for (var ctrl in controllers) ...[
                    GTextField(
                      hintText: 'Todo ${controllers.indexOf(ctrl) + 1}',
                      prefixIcon: Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 20),
                        child: SvgPicture.asset(
                          GImageStrings.clipboard,
                          width: 25,
                        ),
                      ),
                      suffixIcon: IconButton(
                          // Delete Button
                          onPressed: () {
                            setState(() {});
                            removeTextField(controllers.indexOf(ctrl));
                          },
                          icon: const Icon(Icons.clear)),
                      controller: ctrl,
                    ),
                    const SizedBox(
                      height: 15,
                    ),
                  ],

                  // Add Button
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      SizedBox(
                        width: 200,
                        child: ElevatedButton(
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.blue.withOpacity(0.2),
                            ),
                            onPressed: () {
                              setState(() {
                                addTextField();
                              });
                            },
                            child: const Row(
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Icon(Icons.add_circle),
                                SizedBox(
                                  width: 10,
                                ),
                                Text(
                                  'Add',
                                  style: TextStyle(color: Colors.white),
                                )
                              ],
                            )),
                      ),
                    ],
                  )
                ],
              ),
            ),
          )
        ],
      ),
    );
  }

  void addTextField() {
    controllers.add(TextEditingController());
  }

  void removeTextField(int index) {
    controllers.removeAt(index);
  }
}

and this is my custom textformfield

import 'package:flutter/material.dart';

class GTextField extends StatelessWidget {
  final Widget? prefixIcon;
  final Widget? suffixIcon;
  final String hintText;
  final TextEditingController controller;
  const GTextField({
    super.key,
    this.prefixIcon,
    this.suffixIcon,
    required this.hintText,
    required this.controller,
  });

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      decoration: InputDecoration(
        hintText: hintText,
        contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
        prefixIcon: prefixIcon,
        prefixIconConstraints: const BoxConstraints(),
        suffixIcon: suffixIcon,
        filled: true,
        fillColor: Colors.white10.withOpacity(0.05),
        border: OutlineInputBorder(
          borderSide: BorderSide.none,
          borderRadius: BorderRadius.circular(30),
        ),
        errorBorder: OutlineInputBorder(
          borderSide: const BorderSide(color: Colors.red),
          borderRadius: BorderRadius.circular(30),
        ),
      ),
    );
  }
}

before trying to delete ‘study’

after trying to delete ‘study’

I was expecting that the textformfield corrosponding to its controller will be deleted.. but what actually happens is that the last textformfield gets deleted.

3

Answers


  1. TextFormField is missing a controller property.

    Please assign a controller to it.

    ...
    return TextFormField(
      controller: controller,
      decoration: ...,
    );
    ...
    
    Login or Signup to reply.
  2.   List<TextEditingController> _controllers = [TextEditingController()];
     Scaffold(
             
              body: 
              ListView.builder(
                itemCount: _controllers.length,
                itemBuilder: (context, index) {
                  return Padding(
                    padding: EdgeInsets.symmetric(horizontal: 20, vertical: 10),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextFormField(
                            controller: _controllers[index],
                            decoration: InputDecoration(
                              hintText: 'Enter text',
                              border: OutlineInputBorder(),
                            ),
                          ),
                        ),
                        IconButton(
                          icon: Icon(Icons.remove),
                          onPressed: () {
                            setState(() {
                              _controllers.removeAt(index).dispose();
                            });
                          },
                        ),
                      ],
                    ),
                  );
                },
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: () {
                  setState(() {
                    _controllers.add(TextEditingController());
                  });
                },
                child: Icon(Icons.add),
              ),
            )
      @override
      void dispose() {
       
        for (var controller in _controllers) {
          controller.dispose();
        }
        super.dispose();
      }
    Enjoy..
    
    Login or Signup to reply.
  3. you are just missing the to link the controllers in the widget class.

    Here I have modified your code.

    import 'package:flutter/material.dart';
    
    class GTextField extends StatelessWidget {
      final Widget? prefixIcon;
      final Widget? suffixIcon;
      final String hintText;
      final TextEditingController controller;
      const GTextField({
        Key? key,
        this.prefixIcon,
        this.suffixIcon,
        required this.hintText,
        required this.controller,
      }) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return TextFormField(
          decoration: InputDecoration(
            hintText: hintText,
            contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15),
            prefixIcon: prefixIcon,
            prefixIconConstraints: const BoxConstraints(),
            suffixIcon: suffixIcon,
            filled: true,
            fillColor: Colors.white10.withOpacity(0.05),
            border: OutlineInputBorder(
              borderSide: BorderSide.none,
              borderRadius: BorderRadius.circular(30),
            ),
            errorBorder: OutlineInputBorder(
              borderSide: const BorderSide(color: Colors.red),
              borderRadius: BorderRadius.circular(30),
            ),
          ),
     //Just add this link to your widgit class.
         controller: controller,
        );
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search