我基本上尝试做的(我做了,细节在前面)是创建一个包含几个窗体的屏幕,窗体的内容可以使用一个按钮(NEXT)更改。所以我有3个窗体,当我按下next时,我保存第一个窗体并传递给另一个窗体,当我将类放在main. dart中时,我到目前为止所做的工作都是有效的。例如,下面是我的main.dart内容。
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Entries'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
static final _scafoldKey = new GlobalKey<ScaffoldState>();
final firstForm = new FirstForm(title: "First Form");
final secondForm = new SecondForm(title: "First Form",);
final thirdForm = new ThirdForm(title: "First Form");
final Map<int,Widget> forms = {};
final Map<String,Map> allValues = new Map();
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget currentForm;
final Firestore _fireStore = Firestore.instance;
Map<String,Map> thirdFormNested = new Map();
int thirdFormPos = 1;
@override
void initState() {
widget.forms[0] = widget.firstForm;
widget.forms[1] = widget.secondForm;
widget.forms[2] = widget.thirdForm;
currentForm = widget.forms[0];
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: MyHomePage._scafoldKey,
appBar: AppBar(
title: Text(widget.title),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.of(context).pop(),
),
),
body: SingleChildScrollView(
child: currentForm,
),
floatingActionButton: Stack(
children: <Widget>[
Align(
alignment: Alignment(0.4, 1),
child: SizedBox(
width: 75,
child: FloatingActionButton(
shape: RoundedRectangleBorder(),
child: Text("NEXT"),
onPressed: () {
GlobalKey<FormState> key;
if(currentForm is FirstForm) {
key= widget.firstForm.formKey;
if(key.currentState.validate()){
key.currentState.save();
widget.allValues["First Form"] = widget.firstForm.values;
setState(() {
currentForm = widget.forms[1];
});
}
}
else if(currentForm is SecondForm) {
setState(() {
currentForm = widget.forms[2];
});
}
else if (currentForm is ThirdForm) {
key = widget.thirdForm.formKey;
if(key.currentState.validate()){
key.currentState.save();
var tmp = widget.thirdForm.values;
widget.thirdForm.values = <String,String>{};
thirdFormNested.addAll({"Bit $thirdFormPos":tmp});
key.currentState.reset();
widget.thirdForm.build(context);
thirdFormPos++;
}
}
},
),
),
),
Align(
alignment: Alignment(1, 1),
child: SizedBox(
width: 75,
child: FloatingActionButton(
shape: RoundedRectangleBorder(),
child: Text("SUBMIT"),
onPressed: () async {
GlobalKey<FormState> key;
if(currentForm is FirstForm) {
key= widget.firstForm.formKey;
if(key.currentState.validate()) {
key.currentState.save();
await _fireStore.collection('form').document(Uuid().v1()).setData(widget.firstForm.values);
}
}else if(currentForm is SecondForm) {
await _fireStore.collection('form').document(Uuid().v1()).setData(widget.firstForm.values);
}else if(currentForm is ThirdForm) {
key= widget.thirdForm.formKey;
if(key.currentState.validate()){
key.currentState.save();
if(thirdFormNested.length == 0) {
var tmp = widget.thirdForm.values;
widget.thirdForm.values = <String,String>{};
thirdFormNested.addAll({"Bit 1":tmp});
}else {
var tmp = widget.thirdForm.values;
widget.thirdForm.values = <String,String>{};
thirdFormNested.addAll({"Bit $thirdFormPos":tmp});
}
widget.allValues["Third Form"] = thirdFormNested;
await _fireStore.collection('form').document(Uuid().v1()).setData(widget.allValues);
}
}
},
),
),
),
],
)
);
}
}
而这是第一种形式(其他形式都是一样的,输入是唯一的区别):
class FirstForm extends StatelessWidget {
FirstForm({Key key, this.title}) : super(key: key);
final String title;
final GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
Map<String,String> _values = new Map();
get formKey => _formKey;
get values => _values;
set values(v) => _values = v;
@override
Widget build(BuildContext context) {
print("FORM KEY: ${_formKey.currentState}");
_values['Spud Date'] = _formatDate(DateTime.now().toString());
_values['TD Date'] = _formatDate(DateTime.now().toString());
return Form(
key: _formKey,
child: Column(
children: <Widget>[
ListTile(
leading: Text(
"API#",
style: TextStyle(
fontSize: 18,
),
),
title: TextFormField(
decoration: InputDecoration(
hintText: "<api>"
),
validator: (value) {
if(value.isEmpty) {
return "Please enter API value!";
}
return null;
},
onSaved: (value) {
_values['API#'] = value;
},
),
..... Other inputs
Padding(
padding: EdgeInsets.symmetric(vertical: 36.0),
)
],
),
);
}
String _formatDate(String date) {
DateTime d = DateTime.parse(date);
return DateFormat("dd-MM-yyyy").format(d);
}
}
因此,每个表单都有一个表单键,我将其传递给表单(键:我示例化MyHomePage类中的每个表单,并使用widget.firstForm.formKey
检索表单密钥,以便使用它进行验证。
到目前为止,这一切都很顺利。但是,当我试图将我的工作融入现有应用程序时,情况就不一样了。
在现有的应用程序中,我有一个抽屉。抽屉中的一个项目叫做Forms,它把我带到“MyHomePage”,我现在把它重命名为Forms。所以代码还是一样的,我唯一删除的是runApp()和MyApp类,它们现在在一个不同的文件中。下面是抽屉项目代码:
ListTile(
title: new Text('Forms'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
maintainState: true,
builder: (context) => Forms(title: "Forms")
),
);
},
),
现在,使用这个方法。当在表单屏幕中单击“下一步”时,我得到错误:
对null调用了validate()
几个小时后,我发现实际上是表单的GlobalKey返回了一个null状态。问题是为什么?我该如何解决这个问题。
TL;DR:我有3个表单,它们有各自的GlobalKeys。这些键用于验证另一个小部件中的表单,该小部件 Package 了这些表单。当我在main. dart的默认MyHomePage类中有 Package 器小部件时,我创建表单的方法是有效的。但是,当将相同的 Package 器类移动到外部文件并将该类重命名为更合适的名称时,GlobalKeys状态现在为null。
编辑:我注意到的一个额外的尴尬行为是,当我填写表单(FirstForm)并在运行应用程序时第一次按下Next时,我得到了null错误。然而,在第一次运行应用程序时,如果我没有填写表单就按下Next,验证程序就会工作,并向我显示填写输入时的错误。
7条答案
按热度按时间mdfafbf11#
您需要将密钥传递到scaffold小部件中,然后设置上下文和状态。
...
...
...
wqnecbli2#
如果有人有同样的问题,我还没有找到解决这个问题的方法,或者至少没有解释为什么会发生这种情况。但是,我从使用我自己的方法切换到使用继承的小部件来传输我的表单数据,同时保持每个单独的表单内的验证(我在 Package 器类Forms内验证,现在我在每个表单自己的类中验证)。
bf1o4zei3#
有一个非常相似的问题,我使用了一个带有
GlobalKey<SliverAnimatedListState>
的SliverAnimatedList,第一次点击整个小部件树时,小部件绘制完美。这是因为在数据到来之前,我绘制了一个空列表,这个操作初始化了GlobalKey
。第二次currentState为null时,我已经有了数据,但小部件不可见。
我讨厌用这种方式解决问题,但是...我引入了10毫秒的小延迟
我试过用1ms,也能用。
我的整个小部件代码,以防万一它可以帮助你们。
93ze6v8z4#
另一个解决方案是
1.将key注入到
Child({Key? key}) : super(key: key);
这样的子小部件构造函数中1.并将定义为
final GlobalKey<ChildState> _key = GlobalKey();
的部件从父部件body: Center(child: Child(key: _key))
传递到子部件vql8enpb5#
你需要找到解决方案,为什么键变成空?你的小部件得到破坏?如果是的,你如何存储你的小部件状态或你可以使用全局状态?
qaxu7uf26#
由于以下原因,当前状态为空
1.在树中没有匹配该全局关键字的窗口小部件,
1.该小部件不是StatefulWidget,或者相关联的State对象不是T的子类型。
你使用的是StatelessWidget,所以它是null
krcsximq7#
将此更改自
到
应该可以