给定有状态小部件,以某种方式可以调用State类中定义的方法( 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(){...}
}
EDIT :一些真实的代码
首先,我们认为相当于内部小部件;这是一个自定义的文本字段 . 关键是我想根据boolean _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);
}
最后,相当于我的outter小部件 . 它只是一个复选框,后跟prevoius EntradaEmail
小部件 . 据我所知,一旦按下复选框并进行onChange调用,setState调用应重建所有内容,但我与调试消息对比,从未调用第一个内部窗口小部件的构建方法 . 我的观点是根据复选框启用和禁用文本字段 .
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()
],
);
}
}
2 回答
好的,既然您已添加示例代码,我将尝试解释您的窗口小部件无法正常工作的原因,并且我将尝试解释可以进行哪些其他改进 .
首先,您可以通过为所有小部件使用命名构造函数来提高代码的可读性,例如在我的其他答案中(您可以使用Android Studio自动生成它们:定义一些最终字段,然后按灯泡按钮生成构造函数) .
下一个问题是创建
TextEditingController
的小部件必须始终是有状态的小部件!否则,用户输入的内容将在每次构建后消失!通常你会从父窗口小部件传递
TextEditingController
(在提交时处理进程数据的窗口小部件)此外,不鼓励扩展小部件 . 相反,使用组合物,例如:
窗口小部件属性应始终是公共的和最终的(永远不要以
_
开头) .你在
CampoEnvio
做了一些奇怪的事情 .首先,由于某种原因,您将窗口小部件的所有属性传递给
createState
中的State
. 这有一些你可能不打算的后果 .通常情况下,
State
类具有构造函数参数,并且通常会将属性窗口小部件中的属性传递给状态 .问题是
createState
只被调用一次,当你在父窗口小部件中调用initState
时,它不再被调用 . 保持状态直到小部件被丢弃 .这意味着你的状态构造函数也只被调用一次,
_State
(CampoEnvio
)中的字段将始终保持不变 . 即使重建父级并再次调用CampoEnvio
的构造函数,_State
中的旧值也不会被替换 .您在
StatefulWidget
中创建小部件(EntradaMail
和EntradaMovil
)也非常困难 .扩展
StatefulWidget
的类不应该这样做!它基本上只是一个"bag"属性 .以下是完整的固定示例代码,遵循上述约定:
查看Flutter存储库中的官方示例总是有帮助的!
是的,理论上可以使用
GlobalKey
, but not recommended!更好的方法:相反,拉动状态是个好主意:
如果可以,使内部小部件无状态:
如果您的孩子是交互式的(点击,复选框...),您可以使用
VoidCallback
或ValueChanged<T>
(或您自己的typedef
)定义回调来处理父窗口小部件中的事件 .