Как вызвать setState вне класса / виджет с сохранением состояния?

#dart #flutter #state

#dart #флаттер #состояние

Вопрос:

У меня есть следующие переменные

 String _warningMessage;
bool _warningVisibility;
  

Который я хочу обновить с помощью класса, который реализует интерфейс

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

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

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

с помощью кода _displayWarning (который находится внутри _SignupSelectUsernamePageState )

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

Однако всякий раз, когда я вызываю _displayWarning(message) извне _SignupSelectUsernamePageState . Я получаю сообщение об ошибке

 Unhandled Exception: setState() called in constructor
  

Есть ли правильный способ обновления состояний этих переменных вне их класса? Который в моем случае я вызываю _displayWarning(message) из другого класса, который реализует интерфейс

Комментарии:

1. Ну, эти значения в основном изменяются только классом _UserSignupInterface, поэтому, я думаю, можно с уверенностью сказать, что они изменены извне. Моя главная проблема заключается в изменении их состояния, поскольку мне действительно нужно изменить их в рамках реализованных методов интерфейса

2. извините, я нажал кнопку отправки в комментарии до того, как ответ был готов! 😀

3. Для тех, кто читает это и у кого возникает тот же вопрос. Узнайте, как использовать пакет provider, или пакет block, или другие пакеты управления состоянием. Это будет очень полезно, особенно в долгосрочной перспективе, и вам почти никогда не понадобится использовать setState снова. Новичкам может быть трудно понять, но поверьте мне. Этому очень стоит научиться, если вы планируете использовать flutter в течение длительного времени или в проектах среднего и крупного масштаба.

Ответ №1:

Вы должны решить, является ли это значением, которое изменяется внутри виджета, или это значение, которое изменяется по отношению к нему извне.

Если это внутренний, обычно их помещают в State класс с _ на них, они могут начинаться, например, со значения, установленного на initState , и каждый раз, когда они меняются, вы вызываете, setState чтобы указать это.

Однако, если они изменяются вне виджета, то вы помещаете их в StatefulWidget класс (как вы, кажется, и сделали), вы оставляете их без _, поскольку они на самом деле общедоступны, и вы даже делаете их окончательными и помещаете их в конструктор, чтобы разрешить их установку.

В этом последнем случае, если в State классе вы должны быть осведомлены об изменении виджета, вы можете реализовать didUpdateWidget , но это не обязательно.

Конечно, вы можете смешать обе вещи, имея _warningMessage в State , чтобы вы могли обновить его с помощью setState , но с начальным значением, определенным в initState , которое поступает из виджета.

Опять же, если виджет изменяется извне, вы можете снова обновить значение _warningMessage новым значением widgets.

Что-то вроде этого: (я не тестировал этот код)

 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";
            });
          }
        )
      ],
    );
  }
}
  

Итак, в этом примере вы можете изменить warningMessage внешне, например, в parent виджете вы можете передать другое сообщение. Однако, если вам нужно, вы также можете установить его внутренне, используя setState , как это происходит в кнопке onPressed .

Что вы могли бы проверить, действительно ли вам нужно, чтобы это свойство отображалось в Widget , возможно, вам это не нужно! Тогда пример будет выглядеть следующим образом:

 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";
            });
          }
        )
      ],
    );
  }
}
  

Комментарии:

1. Спасибо, однако, я все еще немного сбит с толку. Потому что, если я использую set state, я получаю ошибку, которую я указал в своем вопросе. Должен ли я вызывать конструктор вместо setState?

2. Это сработало бы, поскольку я пробовал это раньше. Но, поскольку я использую класс, который реализует интерфейс, чтобы, надеюсь, управлять состоянием, он выдает мне ошибку. В вопросе указаны как интерфейс, так и ошибка. Но спасибо вам за помощь. Я все еще выясняю способы фактического изменения состояний из интерфейса

3. Затем поместите вашу переменную в UserSignupInterface, неправильно поместить ее в StatefulWidget.

Ответ №2:

Просто создайте статическое значение в состоянии вашего класса виджетов, затем, когда вы создадите виджет, установите его значение для виджета. Поэтому всякий раз, когда вы хотите вызвать его на setState() , просто вызывайте статическое значение.