sajjad sohrabi
sajjad sohrabi

Reputation: 306

Every time i press remove button it removes the last item

When plus button pressed , new From be added to the screen ,and every form has own remove button that call the removeUserForm function form HomePageState class. but the problem is when every remove button pressed it removes the last item , but as i expected just the form that owned of remove button should be removed. why this happen?

i uploaded the full code in Github: https://github.com/geek-sajjad/dynamic_form

class _MyHomePageState extends State<MyHomePage> {

 List<UserForm> _userForms = <UserForm>[];

  void removeUserForm(User user){
    var find =_userForms.firstWhere((userForm) => userForm.user == user);
    if(find!=null){
      _userForms.removeAt(_userForms.indexOf(find));
    }
    setState(() {

    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _userForms.add(UserForm(              
              onRemoved: removeUserForm,
              user: User(),
            ));
          });
        },
        child: Icon(Icons.add),
      ),
      body: SafeArea(
        child: ListView.builder(
          padding: EdgeInsets.all(15),
          itemBuilder: (ctx, int index) {
            return _userForms[index];
          },
          itemCount: _userForms.length,
        ),
      ),
    );
  }
}



class UserForm extends StatelessWidget {
  final User user;
  final Function onRemoved;
  UserForm({this.user, this.onRemoved, });  
  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 6,
      margin: EdgeInsets.all(10),
      child: Padding(
        padding: EdgeInsets.all(15),
              child: Column(
          children: <Widget>[
            TextField(
              onChanged: (value){
                this.user.email = value;
              },
              decoration: InputDecoration(
                labelText: "Email",
                border: OutlineInputBorder(),

              ),
            ),
            SizedBox(height: 20,),
            TextField(
              onChanged: (value){
                this.user.name = value;
              },
              decoration: InputDecoration(
                labelText: "Name",
                border: OutlineInputBorder(),
              ),
            ),
            SizedBox(height: 10,),
            RaisedButton(onPressed: (){              
              this.onRemoved(this.user);
            }, child: Text("Remove"),),
          ],
        ),
      ),
    );
  }

}

Upvotes: 0

Views: 457

Answers (1)

F Perroch
F Perroch

Reputation: 2225

First thing, you could use a Column wrapped in a SingleChildScrollView like this. I think it's a bad Idea to use the ListView.builder widget implementation here

child: SingleChildScrollView(
  child: Column(
    children: _userForms,
  ),
),

Next, you have to add a Key to your UserForm widget and set a unique key for all forms you add (an increment for example). Flutter needs this key to know which widget is (re)drawing.

_userForms.add(UserForm(
  key: Key('$uniqueFormIndex'),
  onRemoved: removeUserForm,
  user: User(),
));

Finally, you can simplify your remove method by passing the form itself.

void removeUserForm(UserForm userForm) {
setState(() {
  _userForms.remove(userForm);
});

Change the constructor of your UserForn wiget

UserForm({
  Key key,
  this.user,
  this.onRemoved,
}) : super(key: key);

And change the onRemoved

RaisedButton(
  onPressed: () {
    this.onRemoved(this);
  },
  child: Text("Remove"),
),

Upvotes: 1

Related Questions