Home > Software design >  controlling a dropdown value in flutter
controlling a dropdown value in flutter

Time:10-11

I'm creating a dynamic form in which the user adds more group fields. so initially there is no form but an add button. with this button the user can add as many form fields as they need. this from is a group form consisting two TextFormField and one DropdownButton.

so lets say the user added 4 group forms and filled each form. but then they changed their minds and wanted to remove the second form. when they do that it removes the last index of the listview, but the value is removed correctly at the selected index. for the textfields i can create a list of controllers and dispose them. but how can i do it for the dropdown?

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const Purchased(),
    );
  }
}

class Purchased extends StatefulWidget {
  const Purchased({Key? key}) : super(key: key);

  @override
  State<Purchased> createState() => _PurchasedState();
}

class _PurchasedState extends State<Purchased> {
  List<UserInfo> list = [];
  List<TextEditingController> textControllerList = [];
  List<TextEditingController> textControllerList1 = [];
  

  @override
  void dispose() {
    textControllerList.forEach((element) {
      element.dispose();
    });
    textControllerList1.forEach((element) {
      element.dispose();
    });
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          /// every time you add new Userinfo, it will generate new FORM in the UI
          list.add(UserInfo());
          setState(() {}); // dont forget to call setState to update UI
        },
        child: const Icon(Icons.add),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
                shrinkWrap: true,
                itemCount: list.length,
                itemBuilder: ((context, index) {
                  return Column(
                    children: [
                      const Text('phone'),
                      Text(list[index].phone),
                      const Text('email'),
                      Text(list[index].email),
                      Text('category'),
                      Text(list[index].category)
                    ],
                  );
                })),
          ),
          Expanded(
            child: ListView.builder(
                shrinkWrap: true,
                itemCount: list.length,
                itemBuilder: ((context, index) {
                  TextEditingController controller = TextEditingController();
                  TextEditingController controller1 = TextEditingController();
                  textControllerList.add(controller);
                  textControllerList1.add(controller1);
                  return MyForm(
                      // dont forget use the key, to make sure every MyForm is has identity. to avoid missed build
                      textEditingController: textControllerList[index],
                    textEditingController1: textControllerList1[index],
                      key: ValueKey(index),
                      //pass init value so the widget always update with current value
                      initInfo: list[index],
                      // every changes here will update your current list value
                      onChangePhone: (phoneVal) {
                        if (phoneVal != null) {
                          setState(() {
                            list[index].setPhone(phoneVal);
                          });
                        }
                      },
                    onChangeEmail: (emailVal) {
                        if (emailVal != null) {
                          list[index].setEmail(emailVal);
                          setState(() {});
                        }
                      },
                    onChangeCategory: (categoryVal) {
                        if (categoryVal != null) {
                          list[index].setCategory(categoryVal);
                          setState(() {});
                        }
                      },
                      // every changes here will update your current list value

                      onremove: () {
                        list.removeAt(index);
                        textControllerList.removeAt(index);
                        textControllerList1.removeAt(index);
                        setState(() {});
                      });
                })),
          )
        ],
      ),
    );
  }
}

class MyForm extends StatefulWidget {
  final UserInfo initInfo;
  final Function(String?) onChangePhone;
  final Function(String?) onChangeEmail;
  final Function(String?) onChangeCategory;
  final TextEditingController textEditingController;
  final TextEditingController textEditingController1;

  final VoidCallback? onremove;
  const MyForm({
    super.key,
    required this.initInfo,
    required this.onChangePhone,
    required this.onChangeEmail,
    required this.onChangeCategory,
    required this.onremove,
    required this.textEditingController,
    required this.textEditingController1,
  });

  @override
  State<MyForm> createState() => _MyFormState();
}

class _MyFormState extends State<MyForm> {
  
  List<UserInfo> list = <UserInfo>;
  
  final List<String> category = [
    'Manager',
    'Reception',
    'Sales',
    'Service',
  ];
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(12),
      child: Column(
        children: [
          IconButton(
              onPressed: widget.onremove,
              icon: const Icon(
                Icons.remove,
              )),
          TextFormField(
            controller: widget.textEditingController,
            onChanged: widget.onChangePhone,
          ),
          TextFormField(
            controller: widget.textEditingController1,
            onChanged: widget.onChangeEmail,
          ),
          DropdownButton(
              isExpanded: true,
              hint: const Text(
                'Select Category',
                style: TextStyle(fontSize: 14),
              ),
              icon: const Icon(
                Icons.arrow_drop_down,
                color: Colors.black45,
              ),
              iconSize: 30,
              items: category
                  .map((item) => DropdownMenuItem<String>(
                        value: item,
                        child: Text(
                          item,
                          style: const TextStyle(
                            fontSize: 14,
                          ),
                        ),
                      ))
                  .toList(),
              onChanged: widget.onChangeCategory)
        ],
      ),
    );
  }
}

class UserInfo {
  ///define
  String _phone = '';
  String _email = '';
  String _category = '';

  /// getter
  String get phone => _phone;
  String get email => _email;
  String get category => _category;

  ///setter
  void setPhone(String phone) {
    _phone = phone;
  }
  void setEmail(String email) {
    _email = email;
  }
  void setCategory(String category) {
    _category = category;
  }
}

PLEASE any help is appreciated.

image for what i want to acheive

CodePudding user response:

you could create a class

class GroupForm extends StatefulWidget{
      TextEditingController controller = TextEditingController();
      TextEditingController controller1 = TextEditingController();
      List category = [];
      GroupForm(this.controller ,this.controller1,this.category)

Widget build(){
return Container(
      padding: const EdgeInsets.all(12),
      child: Column(
        children: [
          IconButton(
              onPressed: widget.onremove,
              icon: const Icon(
                Icons.remove,
              )),
          TextFormField(
            controller: widget.textEditingController,
            onChanged: widget.onChangePhone,
          ),
          TextFormField(
            controller: widget.textEditingController1,
            onChanged: widget.onChangeEmail,
          ),
          DropdownButton(
              isExpanded: true,
              hint: const Text(
                'Select Category',
                style: TextStyle(fontSize: 14),
              ),
              icon: const Icon(
                Icons.arrow_drop_down,
                color: Colors.black45,
              ),
              iconSize: 30,
              items: category
                  .map((item) => DropdownMenuItem<String>(
                        value: item,
                        child: Text(
                          item,
                          style: const TextStyle(
                            fontSize: 14,
                          ),
                        ),
                      ))
                  .toList(),
              onChanged: widget.onChangeCategory)
        ],
      ),
    );
}

}

and then you could create a List<GroupForm> to add and remove any object.

CodePudding user response:

In order this code to work you are going to need to install the Provider Package. With this solution using provider, you dont need to worry about the controllers.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(
            create: (_) => FormsProvider(),
          ),
        ],
        child: const Purchased(),
      ),
    );
  }
}

class Purchased extends StatefulWidget {
  const Purchased({Key? key}) : super(key: key);

  @override
  State<Purchased> createState() => _PurchasedState();
}

class _PurchasedState extends State<Purchased> {
  final List<String> category = [
    'Manager',
    'Reception',
    'Sales',
    'Service',
  ];

  @override
  Widget build(BuildContext context) {
    return Consumer<FormsProvider>(
      builder: (context, formsProvider, child) {
        List<Form> formsList = formsProvider.listOfForms;
        return Scaffold(
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              formsProvider
                  .addFormToList(DateTime.now().millisecondsSinceEpoch);
            },
            child: const Icon(Icons.add),
          ),
          body: Column(
            children: [
              Expanded(
                child: ListView.builder(
                    shrinkWrap: true,
                    itemCount: formsList.length,
                    itemBuilder: ((context, index) {
                      UserInfo formItemInfo = formsList[index].userInfo;
                      return Column(
                        children: [
                          const Text('phone'),
                          Text(formItemInfo.phone),
                          const Text('email'),
                          Text(formItemInfo.email),
                          const Text('category'),
                          Text(formItemInfo.category)
                        ],
                      );
                    })),
              ),
              Expanded(
                child: ListView.builder(
                    shrinkWrap: true,
                    itemCount: formsList.length,
                    itemBuilder: ((context, index) {
                      Form form = formsList[index];
                      return Container(
                        padding: const EdgeInsets.all(12),
                        child: Column(
                          children: [
                            IconButton(
                              onPressed: () {
                                formsProvider.removeFormFromList(form);
                              },
                              icon: const Icon(
                                Icons.remove,
                              ),
                            ),
                            TextFormField(
                              onChanged: (phoneVal) {
                                formsProvider.setPhone(form.id, phoneVal);
                              },
                            ),
                            TextFormField(
                              onChanged: (emailVal) {
                                formsProvider.setEmail(form.id, emailVal);
                              },
                            ),
                            DropdownButton(
                              isExpanded: true,
                              hint: const Text(
                                'Select Category',
                                style: TextStyle(fontSize: 14),
                              ),
                              icon: const Icon(
                                Icons.arrow_drop_down,
                                color: Colors.black45,
                              ),
                              iconSize: 30,
                              items: category
                                  .map((item) => DropdownMenuItem<String>(
                                        value: item,
                                        child: Text(
                                          item,
                                          style: const TextStyle(
                                            fontSize: 14,
                                          ),
                                        ),
                                      ))
                                  .toList(),
                              onChanged: (categoryVal) {
                                if (categoryVal != null) {
                                  formsProvider.setCategory(
                                      form.id, categoryVal);
                                }
                              },
                            )
                          ],
                        ),
                      );
                    })),
              )
            ],
          ),
        );
      },
    );
  }
}

class FormsProvider extends ChangeNotifier {
  List<Form> _listOfForms = [];
  List<Form> get listOfForms => _listOfForms;

  void addFormToList(int id) {
    _listOfForms.add(
        Form(id: id, userInfo: UserInfo(category: '', email: '', phone: '')));
    notifyListeners();
  }

  void removeFormFromList(Form form) {
    _listOfForms.remove(form);
    notifyListeners();
  }

  void setEmail(int idForm, String newEmail) {
    _listOfForms.firstWhere((element) => element.id == idForm).userInfo.email =
        newEmail;
    notifyListeners();
  }

  void setPhone(int idForm, String newPhone) {
    _listOfForms.firstWhere((element) => element.id == idForm).userInfo.phone =
        newPhone;
    notifyListeners();
  }

  void setCategory(int idForm, String newCategory) {
    _listOfForms
        .firstWhere((element) => element.id == idForm)
        .userInfo
        .category = newCategory;

    notifyListeners();
  }
}

class Form {
  int id;
  UserInfo userInfo;

  Form({
    required this.id,
    required this.userInfo,
  });
}

class UserInfo {
  String phone;
  String email;
  String category;

  UserInfo({
    this.email = '',
    this.phone = '',
    this.category = '',
  });
}

  • Related