首页 > 解决方案 > 从 State 类调用方法

问题描述

给定一个有状态的小部件,以某种方式可以调用在 State 类中定义的方法(即 which extends State<NameOfTheWidget>)。实际上,我只是想重建 _State 类,就像调用 setState() 但从类外部。我知道如何从孩子到父母,但反之亦然。

class Foo extends StatefulWidget{
  State createState() => new _State();
  //...bar() ??
}

class _State extends State<Foo>{
  @override
  Widget build(BuildContext context) {...}

  void bar(){...}
}

编辑:一些真实的代码

首先,我们拥有内部小部件的等价物;这是一个自定义的文本字段。关键是我想根据布尔 _activo 变量启用和禁用它。

import 'package:flutter/material.dart';
import 'package:bukit/widgets/ensure.dart';

class EntradaDatos extends StatelessWidget{
  final String _titulo;
  final String _hint;
  TextEditingController _tec;
  FocusNode _fn = new FocusNode();
  final String Function(String s) _validador;
  final TextInputType _tit;
  bool _activo;

  /*
   *  CONSTRUCTOR
   */
  EntradaDatos(this._titulo, this._hint, this._validador, this._tit, this._activo){
    _tec = new TextEditingController();     
  }

  @override
  Widget build(BuildContext context){
    print('Construyendo');
    return new EnsureVisibleWhenFocused(
      focusNode: _fn,
      child: new TextFormField(
        enabled: _activo,
        keyboardType: _tit,
        validator: _validador,
        autovalidate: true,
        focusNode: _fn,
        controller: _tec,
        decoration: InputDecoration(
          labelText: _titulo,
          hintText: _hint
        ),
      )
    );
  }

  String getContenido(){
    return _tec.text;
  }
}

然后我有一个前一个文本字段的具体实现,它只是扩展了它:

import 'package:flutter/material.dart';
import 'package:bukit/widgets/entrada_datos.dart';

class EntradaMail extends EntradaDatos{

  static String _hint = "nombre@dominio.es";
  static String _validador(String s){
    if(s.isEmpty){
      return 'El campo es obligatorio';
    }else{
      if(!s.contains('@') || !s.contains('.') || s.contains(' ')){
        return 'Introduce una dirección válida';
      }else{
        String nombre = s.substring(0, s.indexOf('@'));
        String servidor = s.substring(s.indexOf('@')+1, s.lastIndexOf('.'));
        String dominio = s.substring(s.lastIndexOf('.')+1);
        if(nombre.length < 2 || servidor.length < 2 || dominio.length < 2){
          return 'Introduce una dirección válida';
        }
      }
    }
  }

  EntradaMail(String titulo, bool activo) : super(titulo, _hint, _validador, TextInputType.emailAddress, activo);
}

最后,相当于我的外部小部件。它只是一个复选框,后跟 prevoiusEntradaEmail小部件。据我所知,一旦按下复选框并进行 onChange 调用,setState 调用应该重建所有内容,但我与调试消息形成对比,即永远不会调用第一个内部小部件的 build 方法。我的观点是根据复选框启用和禁用文本字段。

class CampoEnvio extends StatefulWidget{

  EntradaMail _mail;
  EntradaMovil _movil;
  String _tituloMail;
  String _tituloMovil;  
  bool _usaMail = false;
  bool _usaMovil = false;

  CampoEnvio(this._tituloMail, this._tituloMovil){
    _mail = new EntradaMail(_tituloMail, _usaMail);
    _movil = new EntradaMovil(_tituloMovil, _usaMovil);
  }

  State createState() => _State(_mail, _movil, _usaMail, _usaMovil, _tituloMail, _tituloMovil);

}

class _State extends State<CampoEnvio>{

  bool _usaMail;
  bool _usaMovil;
  String _tituloMail;
  String _tituloMovil;
  EntradaMail _mail;
  EntradaMovil _movil;

  _State(this._mail, this._movil, this._usaMail, this._usaMovil, this._tituloMail, this._tituloMovil);


  @override
  Widget build(BuildContext context){
    return new Column(
      children: <Widget>[
        new ListTile(
          leading: new SizedBox(
            width: 70.0,
            child: new Row(
              children: <Widget>[
                new Checkbox(
                  value: _usaMail,
                  activeColor: Colors.black,
                  onChanged: (value) {
                    setState(() {
                      _usaMail = value;                 
                    });
                  },
                ),
              ],
            ),
          ),
          title: _mail,
        ),
        //...
        new Divider()
      ],
    );
  }
}

标签: flutter

解决方案


是的,理论上可以使用 a GlobalKey但不推荐!

class OuterWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => OuterWidgetState();
}

class OuterWidgetState extends State<OuterWidget> {
  final _innerKey = GlobalKey<InnerWidgetState>();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        InnerWidget(key: _innerKey),
        RaisedButton(
          child: Text('call foo'),
          onPressed: () {
            _innerKey.currentState.foo();
          },
        )
      ],
    );
  }
}

class InnerWidget extends StatefulWidget {
  InnerWidget({Key key}) : super(key: key);

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

class InnerWidgetState extends State<InnerWidget> {
  String _value = 'not foo';

  @override
  Widget build(BuildContext context) {
    return Text(_value);
  }

  void foo() {
    setState(() {
      _value = 'totally foo';
    });
  }
}

更好的方法:相反,提升状态是个好主意:

class OuterWidget extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => OuterWidgetState();
}

class OuterWidgetState extends State<OuterWidget> {
  String _innerValue;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: <Widget>[
        InnerWidget(value: _innerValue),
        RaisedButton(
          child: Text('call foo'),
          onPressed: () {
            setState(() {
              _innerValue = 'totally foo';
            });
          },
        )
      ],
    );
  }
}

class InnerWidget extends StatefulWidget {
  InnerWidget({Key key, this.value}) : super(key: key);

  final String value;

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

class InnerWidgetState extends State<InnerWidget> {
  @override
  Widget build(BuildContext context) {
    return Text(widget.value);
  }
}

如果可以的话,使内部小部件无状态:

class InnerWidget extends StatelessWidget {
  InnerWidget({Key key, this.value}) : super(key: key);

  final String value;

  @override
  Widget build(BuildContext context) {
    return Text(value);
  }
}

如果您的孩子是交互式的(点击、复选框...),您可以使用VoidCallbackValueChanged<T>(或您自己的typedef)定义回调来处理父小部件中的事件。


推荐阅读