Передача даних у віджет Stateful


113

Мені цікаво, яким є рекомендований спосіб передачі даних у віджет, що створює стан, під час його створення.

Два стилі, які я бачив:

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а замість цього у віджеті.

Чи є найкраща практика для цього?


3
Конструктор можна звести доServerInfo(this._server);
Гюнтера Зехбауера

Це питання було раніше запитав: stackoverflow.com/questions/50428708 / ...
Blasanka


Ця відповідь додається один місяць до цієї: stackoverflow.com/questions/50428708 / ...
Blasanka

Відповіді:


231

Не передайте параметри за 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
І ось я і не розумію коментар. "Не передайте параметри State, використовуючи його конструктор". Тож як я можу передати параметри Державі?
Хофі

6
@Rexford Stateвже як доступ до всіх властивостей Statefulза допомогою widgetполя.
Ремі Русселет

4
@ RémiRousselet Що робити, якщо я хочу використовувати foo для попереднього заповнення текстового поля, і все ж дозволяю користувачеві його редагувати. Чи слід також додати ще одну власність foo у штаті?
Саїфі Саїфі

1
@ user6638204 Ви можете створити інше властивість foo у державі та змінити режим, void initState()щоб встановити початкове значення. Перевірте цей параметр потоку C як приклад.
Джозеф Ченг

30

Найкращий спосіб - не передавати параметри класу 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

Ще одна відповідь, спираючись на привид @ @ 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, не потрібно переходити до конструктора
Aqib

Як передати це аргументом?
Стієв Джеймс

@SteevJames у віджета MyStatefulє один необов'язковий іменований параметр (властивість), ви можете створити цей віджет, зателефонувавшиMyStateful(foo: "my string",)
Kirill Karmazin

@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);
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.