Rick
Rick

Reputation: 2271

How to call setState outside class/stateful widget?

I have the following variables

String _warningMessage;
bool _warningVisibility;

Which I want to update via a Class which implements an interface

class _UserSignupInterface extends _SignupSelectUsernamePageState
    implements UserSignupInterface {
  @override
  void onSuccess() {
    _hideWarning();
    _navigateToUserPage();
  }

  @override
  void onError(String message) {
    _isSignupClickable = true;

    if(message != null) {
      _displayWarning(message);
    }
  }
}

with the _displayWarning code (which is inside the _SignupSelectUsernamePageState)

void _displayWarning(String message) {
    if (message != null) {
      setState(() {
        widget._warningMessage = message;
        widget._warningVisibility = true;
      });
    }
  }

However, whenever I call the _displayWarning(message) from outside the _SignupSelectUsernamePageState. I get an error saying

Unhandled Exception: setState() called in constructor

Is there a proper way of updating these variable states outside their class? Which in my case, I'm calling the _displayWarning(message) from another class that implements an interface

Upvotes: 7

Views: 10735

Answers (2)

Nimr Sawafta
Nimr Sawafta

Reputation: 647

Just create a static value in the state of your widget class, then when you build the widget, set it's value to the widget. So whenever you want to call it to setState(), just call the static value.

Upvotes: -2

Feu
Feu

Reputation: 5780

You have to decide whether this is a value that is changed internally within the widget, or if that's a value that changes externally to it.

If it's internal, the common thing is to place them in the State class with the _ on them, they could start with a value for instance set on initState and every time they change you call setState to indicate that.

However, if they change outside the widget, then you place them on the StatefulWidget class (as you seem to have done), you leave them without the _ as they are actually public and you even make them final and place them in the constructor to allow them to be set.

In this last case, if in the State class you must be aware of a change in the widget, you can implement didUpdateWidget, but that's not mandatory.

Of course you can mix both things, having a _warningMessage in the State, so you can update it with setState, but with an initial value defined in initState that comes from the widget.

Again, if the widget changes externally, you can again update the value of the _warningMessage with the new widgets value.

Something like that: (I didn't test this code)

class YourWidget extends StatefulWidget {
  YourWidget({this.warningMessage});

  final String warningMessage;

  @override
  State<YourWidget> createState() => new _YourWidgetState();
}

class _YourWidgetState extends State<YourWidget> {
  String _warningMessage;

  @override
  void initState() {
    super.initState();
    _warningMessage = widget.warningMessage;
  }

  @override
  didUpdateWidget(ReorderableListSimple oldWidget) {
    super.didUpdateWidget(oldWidget);
    _warningMessage = widget.warningMessage;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text(_warningMessage),
        RaisedButton(
          child: Text("Change Message"),
          onPressed: () {
            setState(() {
              _warningMessage = "new message from within the State class";
            });
          }
        )
      ],
    );
  }
}

So in this example you can change the warningMessage externally, like in the parent Widget you are able to pass a different message. However, if you need, you can also set it internally using setState, as it's happening in the button's onPressed.

What you might check is wether you actually need that property exposed in the Widget, maybe you don't! Then, the example would look like that:

class YourWidget extends StatefulWidget {
  @override
  State<YourWidget> createState() => new _YourWidgetState();
}

class _YourWidgetState extends State<YourWidget> {
  String _warningMessage;

  @override
  void initState() {
    super.initState();
    _warningMessage = "default message, no need for widget";
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        Text(_warningMessage),
        RaisedButton(
          child: Text("Change Message"),
          onPressed: () {
            setState(() {
              _warningMessage = "new message from within the State class";
            });
          }
        )
      ],
    );
  }
}

Upvotes: 3

Related Questions