Pasar datos a un widget con estado

113

Me pregunto cuál es la forma recomendada de pasar datos a un widget con estado, mientras se crea.

Los dos estilos que he visto son:

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 mantiene un valor en ServerInfoy _ServerInfoState, lo que parece un poco derrochador.

El otro método es utilizar 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;
    }
}

Esto parece un poco al revés, ya que el estado ya no se almacena _ServerInfoSateen el widget, sino que lo hace.

¿Existe una buena práctica para esto?

Mojachiee
fuente
3
El constructor se puede reducir aServerInfo(this._server);
Günter Zöchbauer
Esta pregunta se ha realizado anteriormente: stackoverflow.com/questions/50428708/…
Blasanka
Esta respuesta se agrega un mes antes de esta: stackoverflow.com/questions/50428708/…
Blasanka

Respuestas:

230

No pase parámetros para Stateusar su constructor. Solo debe acceder a estos utilizando this.widget.myField.

No solo editar el constructor requiere mucho trabajo manual; no trae nada. No hay razón para duplicar todos los campos de Widget.

EDITAR:

He aquí un ejemplo:

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
fuente
23
Un comentario adicional, ¡cualquier cosa que pase a un objeto State a través del constructor nunca se actualizará!
Jonah Williams
4
Y aquí estoy y no entiendo el comentario. "No pase parámetros al estado usando su constructor". Entonces, ¿cómo paso los parámetros al estado?
KhoPhi
6
@Rexford Stateya tiene acceso a todas las propiedades de Statefulmediante el uso del widgetcampo.
Rémi Rousselet
4
@ RémiRousselet ¿Qué sucede si quiero usar foo para rellenar previamente un campo de texto y aún permitir que el usuario lo edite? ¿Debo agregar también otra propiedad foo en el estado?
Dijo Saifi
1
@ user6638204 Puede crear otra propiedad foo en el estado y anular void initState()el estado para establecer el valor inicial. Marque esta opción de hilo C como ejemplo.
Joseph Cheng
30

La mejor manera es no pasar parámetros a la clase State usando su constructor. Puede acceder fácilmente en la clase estatal utilizando widget.myField.

Por ejemplo

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

Pasa tus datos cuando navegas por la pantalla:

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

Otra respuesta, basada en la respuesta de @ RémiRousselet y para la pregunta de @ user6638204, si desea pasar valores iniciales y aún poder actualizarlos en el estado más 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
fuente
7
Podemos usar initState directamente para hacer algo como foo = widget.foo, no se requiere pasar al constructor
Aqib
¿Cómo pasar el argumento a esto?
Steev James
@SteevJames el widget MyStatefultiene un parámetro llamado opcional (propiedad) que puede crear este widget llamandoMyStateful(foo: "my string",)
Kirill Karmazin
@Aqib the initStateno resuelve un problema en el siguiente escenario: por ejemplo, creó su widget Statefull con parámetros vacíos y está esperando que se carguen sus datos. Cuando se cargan los datos, desea actualizar su widget Statefull con los datos nuevos y, en este caso, cuando llame a MyStatefull (newData), ¡ initState()no se llamará! En este caso didUpdateWidget(MyStatefull oldWidget), se llamará y necesitará comparar sus datos del argumento oldWidget.getData()con widget.datay si no es el mismo, llamar setState()para reconstruir el widget.
Kirill Karmazin
1
@ kirill-karmazin ¿puedes dar más detalles sobre la solución del widget sin estado? ¿Qué usarías en su lugar? ¿Es una de las mejores prácticas del equipo de Flutter? Gracias
camillo777
4

Para pasar valores iniciales (sin pasar nada al constructor)

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
fuente