BeHappy
BeHappy

Reputation: 3978

A TextEditingController was used after being disposed

I create a form that container inputs. I loop through list of inputs to create form. My variable is:

List fields = [
    {
        'label': Strings.firstName,
        'controller': _fnameController,
        'validator': (val) => Validation.mustFilled(val),
    },
    {
        'label': Strings.lastName,
        'controller': _lnameController,
        'validator': (val) => Validation.mustFilled(val),
    },
    {
        'label': Strings.phoneNumber,
        'controller': _mobileController,
        'validator': (val) => Validation.mobile(val),
    },
];

and my TextEditingController definition:

static TextEditingController _fnameController = TextEditingController();
static TextEditingController _lnameController = TextEditingController();
static TextEditingController _mobileController = TextEditingController();

I get values of inputs from previous screen and set inputs in initState:

@override
void initState() {
    super.initState();
    _fnameController.text = widget.userProfile.firstName;
    _lnameController.text = widget.userProfile.lastName;
    _mobileController.text = widget.userProfile.phoneNumber;
}

for (var item in fields)
    Container(
        margin: EdgeInsets.symmetric(vertical: 10.0),
        child: Input(
            controller: item['controller'],
            label: item['label'],
            validator: item['validator'],
        ),
    ),

and my Input widget:

class Input extends StatelessWidget {
  final String label;
  final VoidFunc validator;
  final TextEditingController controller;
  Input({this.label, this.validator, this.controller});
  @override
  Widget build(BuildContext context) {
    return TextFormField(
      autovalidate: true,
      controller: controller,
      textAlignVertical: TextAlignVertical.center,
      cursorColor: ColorPalette.secondary_3_5,
      decoration: InputDecoration(
        filled: true,
        labelText: label,
      ),
      validator: (value) => validator(value),
    );
  }
}

and finally dispose them:

@override
void dispose() {
    _fnameController.dispose();
    _lnameController.dispose();
    _mobileController.dispose();
    super.dispose();
}

every thing is ok but when I navigate to previous screen (use back button) and then come to this screen again I get this error:

A TextEditingController was used after being disposed.

Once you have called dispose() on a TextEditingController, it can no longer be used.
The relevant error-causing widget was

Upvotes: 11

Views: 35533

Answers (4)

Huzaifa Khan
Huzaifa Khan

Reputation: 179

If you are using a simple text field or text form field then don't dispose the controller and simply initialize it as follow

TextEditingController pinController = TextEditingController();

and if you are using pin code text field then it has its own property, simply use that.

PinCodeTextField(                                              
autoDisposeControllers:false,
)

Upvotes: 4

chunhunghan
chunhunghan

Reputation: 54367

Please remove static keyword from TextEditingController
Change from

static TextEditingController _fnameController = TextEditingController();
static TextEditingController _lnameController = TextEditingController();
static TextEditingController _mobileController = TextEditingController();

To

TextEditingController _fnameController = TextEditingController();
TextEditingController _lnameController = TextEditingController();
TextEditingController _mobileController = TextEditingController();

I use the following full code to reproduce this error.

import 'package:flutter/material.dart';

void main() {
  runApp(MaterialApp(
    title: 'Navigation Basics',
    home: FirstRoute(),
  ));
}

class FirstRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('First Route'),
      ),
      body: Center(
        child: ElevatedButton(
          child: Text('Open route'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => SecondRoute()),
            );
          },
        ),
      ),
    );
  }
}

class SecondRoute extends StatefulWidget {
  @override
  _SecondRouteState createState() => _SecondRouteState();
}

class _SecondRouteState extends State<SecondRoute> {
  static TextEditingController _fnameController = TextEditingController();

  @override
  void dispose() {
    _fnameController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Second Route"),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.pop(context);
          },
          child: Text('Go back!'),
        ),
      ),
    );
  }
}

Upvotes: 6

Rajasekaran K
Rajasekaran K

Reputation: 51

You can override the controller with an empty value by changing the value with onPressed sequence like this

onPressed: () {
 setState(() {
     textEditingController.text = "";
     Navigator.pop(context);
     Navigator.push(
      context,MaterialPageRoute(
            builder: (context) =>const OTPGet()));
       });
   }

This is good if you reload the flow of the PinCodeTextField page again. If you won't revisit the page again, Better you can use void Dispose(){} sequence on initState() {}

TextEditingController textEditingController = TextEditingController(text: "");

PinCodeTextField(
                    appContext: context,
                    autoDisposeControllers: false,
                    length: 6,
                    inputFormatters: <TextInputFormatter>[
                      FilteringTextInputFormatter.allow(RegExp("[0-9]")),
                    ],
                    animationType: AnimationType.fade,
                    pinTheme: PinTheme(
                        borderRadius: BorderRadius.circular(10),
                        shape: PinCodeFieldShape.box,
                        activeColor: Colors.white,
                        selectedColor: Colors.white,
                        inactiveColor: Colors.white,
                        fieldHeight: 50,
                        fieldWidth: 50,
                        activeFillColor: Colors.white,
                        selectedFillColor: Colors.white,
                        inactiveFillColor: Colors.white),
                    cursorColor: Colors.black,
                    animationDuration: const Duration(milliseconds: 300),
                    enableActiveFill: true,
                    controller: textEditingController,
                    keyboardType: TextInputType.number,
                    onChanged: (code) {
                      _onOtpCallBack(code, false);
                    }),


  TextButton(
                        onPressed: () async {
                                Navigator.pop(context);
                                Navigator.push(
                                    context,
                                    MaterialPageRoute(
                                        builder: (context) =>
                                            const OTPGet()));
                          setState(() {
                            textEditingController.text = "";
                          });
                        },
                        child: Text(
                          "resend".tr,
                          style: Styles.sixteenMediumWhiteTextStyle,
                        )),


  bool _isLoadingButton = false;
  bool _enableButton = false;
  _onOtpCallBack(String otpCode, bool isAutofill) {
   setState(() {
   if (otpCode.length == _otpCodeLength) {
      _enableButton = false;
      _isLoadingButton = true;
      _verifyOtpCode();
      otp = otpCode;
      } else {
      _enableButton = false;
    }
   });
  }

 _verifyOtpCode() {
   FocusScope.of(context).requestFocus(FocusNode());
   Timer(const Duration(milliseconds: 4000), () {
    setState(() {
     _isLoadingButton = false;
     _enableButton = false;
     });
   });
  }

Upvotes: 0

Cristian Cruz
Cristian Cruz

Reputation: 627

This solution is only for the people using "pin_code_fields" dependence:

Add or change from this.autoDisposeControllers = true to this.autoDisposeControllers = false

Hope it helps.

Upvotes: 49

Related Questions