flutter - 从 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()
],
);
}
}
解决方案
是的,理论上可以使用 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);
}
}
如果您的孩子是交互式的(点击、复选框...),您可以使用VoidCallback
或ValueChanged<T>
(或您自己的typedef
)定义回调来处理父小部件中的事件。
推荐阅读
- node.js - 地图列表在 DynamoDB 查询中返回未定义
- javascript - 仅 forEach 必填字段
- python - 使用全局创建单例对象导致多次初始化
- php - post请求中的括号
- node.js - Node.js - Http get 不会在 Postman 或浏览器中显示来自 MongoDb 查询的数据
- c# - 获取 CollectionView 返回对象列表并放入 xamarin.form 中的模型 IIList
- r - 将 tag_facet 导出到 .tif
- c# - 按键后如何执行操作?
- java - 运行时应用程序崩溃,模型列表添加了所有对象,ViewPager 适配器似乎很好
- docker - GO - Docker 在 K8S 容器上询问证书