Передача данных в виджет с отслеживанием состояния

115

Мне интересно, каков рекомендуемый способ передачи данных в виджет с отслеживанием состояния при его создании.

Я видел два стиля:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState(_server);
}

class _ServerInfoState extends State<ServerInfo> {
  Server _server;

  _ServerInfoState(Server server) {
    this._server = server;
  }
}

Этот метод сохраняет значение как в ServerInfoи _ServerInfoState, что кажется немного расточительным.

Другой метод - использовать widget._server:

class ServerInfo extends StatefulWidget {

  Server _server;

  ServerInfo(Server server) {
    this._server = server;
  }

  @override
    State<StatefulWidget> createState() => new _ServerInfoState();
}

class _ServerInfoState extends State<ServerInfo> {
  @override
    Widget build(BuildContext context) {
      widget._server = "10"; // Do something we the server value
      return null;
    }
}

Это кажется немного отсталым, поскольку состояние больше не сохраняется, _ServerInfoSateа хранится в виджете.

Есть ли лучший способ для этого?

Mojachiee
источник
3
Конструктор может быть уменьшен доServerInfo(this._server);
Günter Zöchbauer
Этот вопрос задавался ранее: stackoverflow.com/questions/50428708/…
Blasanka
Этот ответ добавлен за месяц до этого: stackoverflow.com/questions/50428708/…
Бласанка

Ответы:

233

Не передавайте параметры Stateего конструктору. Доступ к ним можно получить только с помощью this.widget.myField.

Не только редактирование конструктора требует большого количества ручной работы; это ничего не приносит. Нет причин дублировать все поля Widget.

РЕДАКТИРОВАТЬ :

Вот пример:

class ServerIpText extends StatefulWidget {
  final String serverIP;

  const ServerIpText ({ Key key, this.serverIP }): super(key: key);

  @override
  _ServerIpTextState createState() => _ServerIpTextState();
}

class _ServerIpTextState extends State<ServerIpText> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.serverIP);
  }
}

class AnotherClass extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: ServerIpText(serverIP: "127.0.0.1")
    );
  }
}
Реми Русселе
источник
23
Еще один комментарий: все, что вы передаете объекту State через конструктор, никогда не будет обновлено!
Джона Уильямс
4
И вот я и не понимаю комментария. «Не передавайте параметры в состояние, используя его конструктор». Итак, как мне передать параметры в состояние?
KhoPhi 03
6
@Rexford Stateуже как доступ ко всем свойствам Statefulс помощью widgetполя.
Реми Русселе,
4
@ RémiRousselet Что делать, если я хочу использовать foo для предварительного заполнения текстового поля и по-прежнему позволять пользователю редактировать его. Должен ли я также добавить еще одно свойство foo в состояние?
Сказал Саифи
1
@ user6638204 Вы можете создать другое свойство foo в состоянии и переопределить void initState()состояние, чтобы установить начальное значение. Отметьте этот вариант резьбы C в качестве примера.
Джозеф Ченг
31

Лучший способ - не передавать параметры классу State с помощью его конструктора. Вы можете легко получить доступ к классу State, используя widget.myField.

Например

class UserData extends StatefulWidget {
  final String clientName;
  final int clientID;
  const UserData(this.clientName,this.clientID);

  @override
  UserDataState createState() => UserDataState();
}

class UserDataState extends State<UserData> {
  @override
  Widget build(BuildContext context) {
    // Here you direct access using widget
    return Text(widget.clientName); 
  }
}

Передайте свои данные при навигации по экрану:

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));
Санджайраджсинх
источник
8

Другой ответ, основанный на anwser @RémiRousselet и вопросе @ user6638204, если вы хотите передать начальные значения и все же иметь возможность обновлять их в состоянии позже:

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

  @override
  _MyStatefulState createState() => _MyStatefulState(foo: this.foo);
}

class _MyStatefulState extends State<MyStateful> {
  String foo;

  _MyStatefulState({this.foo});

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}
Николя Дион-Бушар
источник
7
Мы можем напрямую использовать initState, чтобы сделать что-то вроде foo = widget.foo, переход в конструктор не требуется
Акиб,
Как передать этому аргумент?
Стев Джеймс
@SteevJames виджет MyStatefulимеет один необязательный именованный параметр (свойство), вы можете создать этот виджет, позвонивMyStateful(foo: "my string",)
Кирилл Кармазин
@Aqib initStateне решает проблему в следующем сценарии: например, вы создали виджет Statefull с пустыми параметрами и ждете загрузки данных. Когда данные загружены, вы хотите обновить виджет Statefull свежими данными, и в этом случае, когда вы вызываете MyStatefull (newData), он initState()не будет вызываться! В этом случае didUpdateWidget(MyStatefull oldWidget)будет называться , и вам нужно будет сравнить данные из аргументов oldWidget.getData()с , widget.dataи если это не то же самое - вызов setState()для восстановления виджета.
Кирилл Кармазин
1
@ kirill-karmazin, можете ли вы подробнее рассказать о решении для виджетов без сохранения состояния? что бы вы использовали вместо этого? Это лучший опыт команды Flutter? Спасибо
camillo777
4

Для передачи начальных значений (ничего не передавая конструктору)

class MyStateful extends StatefulWidget {
  final String foo;

  const MyStateful({Key key, this.foo}): super(key: key);

  @override
  _MyStatefulState createState() => _MyStatefulState();
}

class _MyStatefulState extends State<MyStateful> {
  @override
  void initState(){
    super.initState();
    // you can use this.widget.foo here
  }

  @override
  Widget build(BuildContext context) {
    return Text(foo);
  }
}
Дакш Шах
источник