Search code examples
flutterflutter-layout

TextField text gets cleared while tapping one of TextField


I am getting some strange issue where one of TextField always gets clears if you tap on it.

class MyEditText extends StatefulWidget {
  static String tag = "MyEditText";
  @override
  MyEditTextState createState() => MyEditTextState();
}

class MyEditTextState extends State<MyEditText> {
  String results = "";
  final TextEditingController controller = new TextEditingController();
  final TextEditingController controller1 = new TextEditingController();

  @override
  Widget build(BuildContext context) {
    final email = TextField(
      decoration: InputDecoration(
          hintText: 'Enter Email',
          contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0)),
    );

    final password = TextField(
      obscureText: true,
      decoration: InputDecoration(
        hintText: 'Enter Password',
        contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
      ),
    );

    return new Scaffold(
      appBar: new AppBar(
        automaticallyImplyLeading: false,
        title: new Text("EditText Sample"),
        backgroundColor: Colors.yellow,
      ),
      body: new Container(
        child: new Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[email, password],
        ),
      ),
    );
  }
}

I am using statful widget for it and all classes from where this screen launch also statful.

Note: If I comment out all TextEditingController and its usage, everything works fine, SO I m not getting what is wrong with TextEditingController


Solution

  • Thanks for the updated code.

    The reason your TextEditingController get cleared is because you declare the variables inside of State<MyEditText>. When the State gets re-initialized - those variables do, too.

    I can see 2 ways to solve this:

    #1 - Move controllers out of the State to the parent class, passing them as arguments

    Controllers are declared and maintained outside of MyEditText widget - in the parent class.

    class MyEditText extends StatefulWidget {
      MyEditText({ Key key, this.emailController, this.passwordController }): super(key: key);
    
      final TextEditingController emailController;
      final TextEditingController passwordController;
    
      static String tag = "MyEditText";
    
      @override
      MyEditTextState createState() => MyEditTextState();
    }
    
    class MyEditTextState extends State<MyEditText> {
      String results = "";
    
      @override
      Widget build(BuildContext context) {
        // ...
        TextField(
          controller: widget.emailController,
          // ...,
        ),
        TextField(
          controller: widget.passwordController,
          // ...,
        ),
        // ...
      }
    }
    

    Then you declare controllers in your parent class and pass them as arguments to MyEditText:

    final emailController = TextEditingController();
    final passwordController = TextEditingController();
    // ...
    MyEditText(
      emailController: emailController,
      passwordController: passwordController,
    )
    

    #2 - Reuse controllers from the old state on didUpdateWidget call

    Controllers can be declared outside of MyEditText class, but if they were not - widget creates and maintains TextEditingController on its own.

    class MyEditText extends StatefulWidget {
      MyEditText({ Key key, this.emailController, this.passwordController }): super(key: key);
    
      final TextEditingController emailController;
      final TextEditingController passwordController;
    
      static String tag = "MyEditText";
    
      @override
      MyEditTextState createState() => MyEditTextState();
    }
    
    class MyEditTextState extends State<MyEditText> {
    
      TextEditingController _emailController;
      TextEditingController _passwordController;
    
      @override
      void initState() {
        super.initState();
        if (widget.emailController == null)
          _emailController = TextEditingController();
        if (widget.passwordController == null)
          _passwordController = TextEditingController();
      }
    
      @override
      void didUpdateWidget(MyEditText oldWidget) {
        super.didUpdateWidget(oldWidget);
    
        if (widget.emailController == null && oldWidget.emailController != null)
          _emailController = TextEditingController.fromValue(oldWidget.emailController.value);
        else if (widget.emailController != null && oldWidget.emailController == null)
          _emailController = null;
    
        if (widget.passwordController == null && oldWidget.passwordController != null)
          _passwordController = TextEditingController.fromValue(oldWidget.passwordController.value);
        else if (widget.passwordController != null && oldWidget.passwordController == null)
          _passwordController = null;
      }
    
      @override
      Widget build(BuildContext context) {
        // ...
        TextField(
          controller: _emailController ?? widget.emailController,
          // ...,
        ),
        TextField(
          controller: _passwordController ?? widget.passwordController,
          // ...,
        ),
        // ...
      }
      // ...
    }
    

    Both methods are similar except that the second one regulates State<MyEditText> variables on its own.

    I will leave it to you to decide which one is more suitable in your case.

    Let me know if this helped.