0

If I update a variable using class object, the build method should get called, but I am unable to call setState from the StatefulWidget class.

class CustomErrorFormField extends StatefulWidget {

  @override
  _CustomErrorFormFieldState createState() {
    return _CustomErrorFormFieldState();
  }

  List<String> errorList = []; //this variable will get updated using below function
    
  void setErrorList(List<String> listOfError) {
        errorList = listOfError;
   }

}
class _CustomErrorFormFieldState extends State<CustomErrorFormField> {


  @override
  void initState() {
    super.initState();
  }


 @override
 Widget build(BuildContext context) {

    print(widget.errorList); //this is not printing updated value

    return .....
  }
}

Now in some other class i will update errorList Variable

 nameTextFild = CustomErrorFormField(
       key: ValueKey(count),
      labelName: "Name",
      iContext: context,
      onChanged: (String value) {


        setState(() {
          count++;
          if (!value.contains(RegExp(r'[0-9]'))) {
            nameTextFild!.setErrorList([]); //updating but changes not appearing (setState of this widget is not getting called)
          } else {
            nameTextFild!.setErrorList(["Invalid characters, use letters only."]);
          }

        });



      },
    );
Siddy Hacks
  • 1,846
  • 14
  • 15
  • instead of `setErrorList` method you have to re-build your `CustomErrorFormField` widget and pass your `errorList` inside `CustomErrorFormField` constructor (like any other widgets do) – pskink Mar 31 '22 at 06:59
  • @pskink rebuilding it will cause the textfield to loose it's content. I want to just validate and send list of error to this custom Textfield which has a list view that can render those list of errors. – Siddy Hacks Mar 31 '22 at 07:11
  • *"rebuilding it will cause the textfield to loose it's content."* - no, it will not – pskink Mar 31 '22 at 07:16

1 Answers1

1

It's not recommended that you change the state of a widget from outside the widget.

What you should do instead is pass the validation logic as a function and let the widget handle the state change.

CustomFormField:

import 'package:flutter/material.dart';

class CustomErrorFormField extends StatefulWidget {
  //Take the validation logic as a parameter.
  final List<String> Function(String value) validator;
  const CustomErrorFormField({required this.validator});

  @override
  _CustomErrorFormFieldState createState() {
    return _CustomErrorFormFieldState();
  }
}

class _CustomErrorFormFieldState extends State<CustomErrorFormField> {
  
  //Keep the state inside the widget itself
  List<String> errorList = [];

  //Update the state from inside the widget
  void setErrorList(List<String> listOfError) {
    setState(() {
      errorList = listOfError;
    });
  }

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      child: TextFormField(
        validator: (String value){
           //Use the validation logic to decide the error.
           setErrorList(widget.validator(value))
          }
        }
      ),
    );
  }
}

I have used TextFormField as an example, you can use any widget that accepts a callback upon change.

If you're making everything from scratch you can attach the validator function to a callback that fires when the text is changed. Usually this is done with the help of a controller.

usage:

final nameTextFild = CustomErrorFormField(
  key: ValueKey(count),
  labelName: "Name",
  iContext: context,
  validator: (String value) {
    if (!value.contains(RegExp(r'[0-9]'))) {
      return [];
    } else {
      return ["Invalid characters, use letters only."];
    }
  },
);
Advait
  • 574
  • 3
  • 12
  • Thanks but actually I wanted CustomErrorFormField as a separate widget that can be used many times with different validation logic. Using validation logic inside the widget will make it a dependent widget. – Siddy Hacks Mar 31 '22 at 06:51
  • 1
    The code you takes validation as well as the state management logic as a parameter. In the code I provided, only the validation logic is taken as a parameter. So you can use the CustormErrorFormField with different validation logic. – Advait Mar 31 '22 at 06:59
  • But see you're setting ("Invalid characters, use letters only.") inside the CustomErrorFormField widget. I want to validate and send this list of errors from outside, so it can render those list of errors. – Siddy Hacks Mar 31 '22 at 07:09
  • 1
    Since you just had an if statement, I made the code with just a `bool` in mind, changed the code to accommodate your needed behaviour. Changing the validator function's return type works . – Advait Mar 31 '22 at 07:22