Passando dados para um widget com estado

113

Estou me perguntando qual é a maneira recomendada de passar dados para um widget com estado, ao criá-lo.

Os dois estilos que vi são:

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;
  }
}

Este método mantém um valor em ServerInfoe _ServerInfoState, o que parece um desperdício.

O outro método é usar 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;
    }
}

Isso parece um pouco para trás, pois o estado não é mais armazenado, _ServerInfoSatemas sim no widget.

Existe uma prática recomendada para isso?

Mojachiee
fonte
3
O construtor pode ser reduzido paraServerInfo(this._server);
Günter Zöchbauer
Este questionamento foi feito anteriormente: stackoverflow.com/questions/50428708/…
Blasanka
Esta resposta é adicionada um mês antes desta: stackoverflow.com/questions/50428708/…
Blasanka

Respostas:

230

Não passe parâmetros para Stateusar seu construtor. Você só deve acessá-los usando this.widget.myField.

Não apenas editar o construtor requer muito trabalho manual; não traz nada. Não há razão para duplicar todos os campos de Widget.

EDITAR:

Aqui está um exemplo:

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")
    );
  }
}
Rémi Rousselet
fonte
23
Um comentário adicional, qualquer coisa que você passar para um objeto State através do construtor nunca será atualizado!
Jonah Williams de
4
E aqui estou eu e não entendo o comentário. "Não passe parâmetros para o Estado usando seu construtor". Então, como faço para passar parâmetros para o Estado?
KhoPhi
6
O @Rexford Statejá tem acesso a todas as propriedades de Statefulusando o widgetcampo.
Rémi Rousselet de
4
@ RémiRousselet E se eu quiser usar foo para pré-preencher um campo de texto e ainda permitir que o usuário o edite. Devo também adicionar outra propriedade foo no estado?
Disse Saifi
1
@ user6638204 Você pode criar outra propriedade foo no estado e substituir void initState()no estado para definir o valor inicial. Verifique esta opção de thread C como exemplo.
Joseph Cheng
30

A melhor maneira é não passar parâmetros para a classe State usando seu construtor. Você pode acessar facilmente na classe State usandowidget.myField .

Por exemplo

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); 
  }
}

Passe seus dados ao navegar na tela:

 Navigator.of(context).push(MaterialPageRoute(builder: (context) => UserData("WonderClientName",132)));
Sanjayrajsinh
fonte
8

Outra resposta, com base na resposta de @RémiRousselet e para a pergunta de @ user6638204, se você deseja passar os valores iniciais e ainda ser capaz de atualizá-los no estado mais tarde:

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);
  }
}
Nicolas Dion-Bouchard
fonte
7
Podemos usar o initState diretamente para fazer algo como foo = widget.foo, não é necessário passar para o construtor
Aqib
Como passar um argumento para isso?
Steev James de
@SteevJames o widget MyStatefultem um parâmetro nomeado opcional (propriedade) que você pode criar este widget chamandoMyStateful(foo: "my string",)
Kirill Karmazin
@Aqib the initStatenão resolve um problema no seguinte cenário: por exemplo, você criou seu widget Statefull com parâmetros vazios e está esperando que seus dados sejam carregados. Quando os dados são carregados, você deseja atualizar seu widget Statefull com os dados novos e, neste caso, quando você chama MyStatefull (newData), ele initState()não será chamado! Neste caso didUpdateWidget(MyStatefull oldWidget), será chamado e você precisaria comparar seus dados do argumento oldWidget.getData()com widget.datae se não forem iguais - chamar setState()para reconstruir o widget.
Kirill Karmazin
1
@ kirill-karmazin você pode elaborar mais sobre a solução de widget sem estado? o que você usaria no lugar? É uma prática recomendada da equipe Flutter? Obrigado
camillo777
4

Para passar valores iniciais (sem passar nada para o construtor)

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);
  }
}
Daksh Shah
fonte