1

I have created a reusable field that is called to display different fields in a form in different screen in my app i have also passed a controller however when dispose the controller it shows this error and if i go back to the same form screen it crashes.

class EntryField extends StatefulWidget {
  @override
  _EntryFieldState createState() => _EntryFieldState();
  final String title;
  final TextEditingController controller;
  final TextInputType inputType;
  final FilteringTextInputFormatter filter;
  final hintText;
  EntryField({@required this.title,this.hintText,@required this.controller,@required this.inputType,@required this.filter});
}
class _EntryFieldState extends State<EntryField> {
  @override
  void dispose() {
    widget.controller.dispose();
    print("anything");
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 10),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Text(
              this.widget.title,
              style: GoogleFonts.quicksand(
                fontSize: 18,
              )
          ),
          SizedBox(
            height: 10,
          ),
          TextFormField(
            controller: this.widget.controller,
            keyboardType: this.widget.inputType,
            inputFormatters: <TextInputFormatter>[
              this.widget.filter,
            ],
            validator: (value){
              if(value.isEmpty){
                return "${this.widget.title} is a Required Field";
              }
              return null;
            },
            decoration: InputDecoration(
              hintText: this.widget.hintText,
              border: InputBorder.none,
              fillColor: Color(0xfff3f3f4),
              filled: true,
              errorBorder: new OutlineInputBorder(
                borderSide: new BorderSide(color: Colors.red),
              ),
              errorStyle: TextStyle(
                fontSize: 15,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

and in this class i am passing it field values

final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final _serviceTitleController = TextEditingController();
final _serviceCategoryController = TextEditingController();
final _servicePriceController = TextEditingController();
ToastErrorMessage _error = ToastErrorMessage();
ToastValidMessage _valid = ToastValidMessage();

class AddServices extends StatefulWidget {
  @override
  _AddServicesState createState() => _AddServicesState();
}

class _AddServicesState extends State<AddServices> {

  int currentIndex;
  String _cityName;
  final WorkshopServiceQueries _add = WorkshopServiceQueries();
  final _firebaseUser = FirebaseAuth.instance.currentUser;

  @override
  void initState() {
    cityName();
    super.initState();
    currentIndex = 0;
  }
  @override
  void dispose() {
    print("hello");
    super.dispose();
  }
  void clearControllerText(){
    _serviceTitleController.clear();
    _serviceCategoryController.clear();
    _servicePriceController.clear();

  }

  Future cityName() async{
   _cityName = await _add.getWorkshopCityName();
  }

  changePage(int index) {
    setState(() {
      currentIndex = index;
    });
  }
  validateFields() async{

    final ValidateWorkshopServices service = ValidateWorkshopServices();
    final int _price = int.tryParse(_servicePriceController.text.trim());
    if(!service.validateServiceCategory(_serviceCategoryController.text.trim()) && !service.validateServiceTitle(_serviceTitleController.text.trim()) && !service.validateServicePrice(_price)){
      _error.errorToastMessage(errorMessage: "Enter Valid Data in Each Field");
    }
    else if(!service.validateServiceCategory(_serviceCategoryController.text.trim())){
      _error.errorToastMessage(errorMessage: "Service Category Must Only contain Alphabets");
    }
    else if(!service.validateServiceTitle(_serviceTitleController.text.trim())){
      _error.errorToastMessage(errorMessage: "Service Title Must Only contain Alphabets");
    }
    else if(!service.validateServicePrice(_price)){
      _error.errorToastMessage(errorMessage: "Service Price must be less than or equal to 2000");
    }
    else{
     await addService(_price);
    }
  }
  Future<void> addService(int price) async{
  try {

      Services data = Services(title: _serviceTitleController.text.trim(), category: _serviceCategoryController.text.trim(), price: price, workshopCity: _cityName, workshopId: _firebaseUser.uid);
      await _add.addWorkshopService(data);
      if(WorkshopServiceQueries.resultMessage == WorkshopServiceQueries.completionMessage){
        _valid.validToastMessage(validMessage: WorkshopServiceQueries.resultMessage);
        clearControllerText();
        Future.delayed(
          new Duration(seconds: 2),
              (){
            Navigator.pop(context);
          },
        );
      }
      else{
        _error.errorToastMessage(errorMessage: WorkshopServiceQueries.resultMessage);
      }
    }catch(e){
       _error.errorToastMessage(errorMessage: e.toString());
    }
}

  @override
  Widget build(BuildContext context) {

    final height = MediaQuery.of(context).size.height;
    int _checkboxValue;
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(
            'BIKERSWORLD',
            style: GoogleFonts.quicksand(
              color: Colors.white,
              fontSize: 18,
            ),
          ),
          backgroundColor: Color(0XFF012A4A),
            leading: IconButton(icon:Icon(Icons.arrow_back, color: Colors.orange,),
              onPressed:() => Navigator.pop(context),
            )
        ),
        body: Container(
          height: height,
          child: Stack(
            children: <Widget>[
              Container(
                padding: EdgeInsets.symmetric(horizontal: 20),
                child: SingleChildScrollView(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      SizedBox(height: 30,),
                      _title(),
                      SizedBox(height: 40),
                      _addServicesWidget(),
                      SizedBox(height: 20),
                      FlatButton(
                      child: Container(
                        padding: EdgeInsets.symmetric(vertical: 15),
                        alignment: Alignment.center,
                        decoration: BoxDecoration(
                            borderRadius: BorderRadius.all(Radius.circular(5)),
                            boxShadow: <BoxShadow>[
                              BoxShadow(
                                  color: Colors.grey.shade200,
                                  offset: Offset(2, 4),
                                  blurRadius: 5,
                                  spreadRadius: 2)
                            ],
                            gradient: LinearGradient(
                                begin: Alignment.centerLeft,
                                end: Alignment.centerRight,
                                colors: [Color(0xfffbb448), Color(0xfff7892b)])),
                        child: Text(
                          'Register Now',
                          style: GoogleFonts.krub(
                            fontSize: 18,
                            color: Colors.white,
                          ),
                        ),
                      ),
                      onPressed: (){
                        if(!_formKey.currentState.validate()){
                          return;
                        }
                        else{
                          validateFields();
                        }
                      },
                    ),
                      SizedBox(height: 20),

                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
Widget _addServicesWidget() {
  return Form(
    key: _formKey,
    autovalidateMode: AutovalidateMode.disabled,
    child: Column(
      children: <Widget>[
        EntryField(title: "Category",hintText: 'Mechanical',controller: _serviceCategoryController,inputType: TextInputType.text,filter: FilteringTextInputFormatter.allow(RegExp("[a-zA-Z ]"))),
        SizedBox(height:15,),
        EntryField(title: "Title",hintText: 'wheel barring',controller: _serviceTitleController,inputType: TextInputType.text,filter: FilteringTextInputFormatter.allow(RegExp("[a-zA-Z ]"))),
        SizedBox(height:15,),
        EntryField(title: "Price",hintText: 'price < 2000',controller: _servicePriceController,inputType: TextInputType.number,filter:FilteringTextInputFormatter.digitsOnly),
      ],
    ),
  );
}

2 Answers2

3

You shouldn't dispose the controller from within your widget, since you are creating it outside the widget and passing a reference to it into the widget.

It looks like your controllers are created in the global scope - if so, and if they are intended to be used throughout the lifetime of the app, you shouldn't dispose them.

So either

  • don't dispose the controllers if they are globals
  • or create and dispose them from the same "owner" object
Magnus
  • 17,157
  • 19
  • 104
  • 189
  • This I one class where I am calling this reusable entry field class but there are many forms in my app where I am using this class should I dispose it or not – Informative Programmer Mar 18 '21 at 13:31
  • @InformativeProgrammer You should dispose it when you no longer need it. After you have disposed it, if you need to use it again, you would need to create a new controller. So if you intend to use the same controller during the entire app, then there is no need to dispose of it. – Magnus Mar 18 '21 at 19:15
  • For different screens containing forms I am using different controllers if I try to dispose of them then it show the error – Informative Programmer Mar 19 '21 at 09:21
  • would that mean that in the onClose method inside getxController we dont dispose the controller. If thats the case i tried it and im still getting the same error when using Get.back – LearnFlutter Jun 02 '22 at 10:58
3

for future comers, in my case i was using dispose twice for the same controller:

//error
void dispose() {
     myController.dispose();
     myController.dispose();
     super.dispose();
  }

//ok
void dispose() {
     myController.dispose();
     super.dispose();
  }
Zaid Alazwi
  • 395
  • 3
  • 8