问题描述
我正在尝试创建动态表单,并使用TextFormField进行验证.
I am trying to create a dynamic form and using TextFormField for validation purpose.
下面是给出错误的代码多个小部件使用相同的GlobalKey或Duplicate Global Key.我不确定如何解决此问题或不确定如何按照标准清洁Dynamic Form.
Below is the code that is giving error Multiple widgets used the same GlobalKey or Duplicate Global key.I am not sure how can i fix this or how can i make Dynamic Form clean as per standard.
import 'package:flutter/material.dart';
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String person;
String age;
String job;
var nameTECs = <TextEditingController>[];
var ageTECs = <TextEditingController>[];
var jobTECs = <TextEditingController>[];
var cards = <Card>[];
var nameController = TextEditingController();
var ageController = TextEditingController();
var jobController = TextEditingController();
@override
void initState() {
super.initState();
cards.add(createCard());
}
Card createCard() {
nameTECs.add(nameController);
ageTECs.add(ageController);
jobTECs.add(jobController);
return Card(
child:new Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Person ${cards.length + 1}'),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: nameController,
decoration: InputDecoration(labelText: 'Full Name'),
validator: validatetext,
onSaved: (String val) {
person = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: ageController,
decoration: InputDecoration(labelText: 'Age'),
validator: validatetext,
onSaved: (String val) {
age = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: jobController,
decoration: InputDecoration(labelText: 'Study/ job'),
validator: validatetext,
onSaved: (String val) {
job = val;
},
),
],
),
),
);
}
void _validateInputs() {
print('button');
if (_formKey.currentState.validate()) {
// If all data are correct then save data to out variables
_formKey.currentState.save();
_onDone();
}
}
_onDone() {
List<PersonEntry> entries = [];
for (int i = 0; i < cards.length; i++) {
var name = nameTECs[i].text;
var age = ageTECs[i].text;
var job = jobTECs[i].text;
entries.add(PersonEntry(name, age, job));
}
Navigator.pop(context, entries);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text('Add new'),
onPressed: () => setState(() => cards.add(createCard())),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text('Remove last'),
onPressed: () => setState(() => cards.removeLast()),
),
)
],
),
floatingActionButton:
FloatingActionButton(child: Icon(Icons.save), onPressed: _validateInputs),
);
}
}
class PersonEntry {
final String name;
final String age;
final String studyJob;
PersonEntry(this.name, this.age, this.studyJob);
@override
String toString() {
return 'Person: name= $name, age= $age, study job= $studyJob';
}
}
String validatetext(String value) {
if (value.length < 5)
return 'More than 5 char is required';
else
return null;
}
万一有人想要完整的错误.
In case someone wants full error.
The following assertion was thrown while finalizing the widget tree:
Multiple widgets used the same GlobalKey.
The key [LabeledGlobalKey<FormState>#89788] was used by multiple widgets. The parents of those widgets were:
- Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#65de2 relayoutBoundary=up10)
- Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#f4085 relayoutBoundary=up10)
A GlobalKey can only be specified on one widget at a time in the widget tree.
When the exception was thrown, this was the stack
#0 GlobalKey._debugVerifyGlobalKeyReservation.<anonymous closure>.<anonymous closure>.<anonymous closure>
package:flutter/…/widgets/framework.dart:246
#1 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)
#2 GlobalKey._debugVerifyGlobalKeyReservation.<anonymous closure>.<anonymous closure>
package:flutter/…/widgets/framework.dart:193
#3 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:379:8)
#4 GlobalKey._debugVerifyGlobalKeyReservation.<anonymous closure>
推荐答案
问题是,当您为所有 forms
使用相同的键 _formKey
时.您可以创建包含 Globalkey< FormState>
的 _formKeys
的 List
,并根据卡的长度在其中添加或删除密钥.
The issue is You're using the same key _formKey
when for all your forms
. You can create a List
of _formKeys
that contains Globalkey<FormState>
and key adding or removing to it based on the length of your cards.
我以您的代码为例添加了一个演示:
I added a demo using your code as an example:
class App extends StatefulWidget {
@override
_AppState createState() => _AppState();
}
class _AppState extends State<App> {
List<GlobalKey<FormState>> _formKeys = [
GlobalKey<FormState>()
]; // create a list of form keys
String person;
String age;
String job;
var nameTECs = <TextEditingController>[];
var ageTECs = <TextEditingController>[];
var jobTECs = <TextEditingController>[];
var cards = <Card>[];
var nameController = TextEditingController();
var ageController = TextEditingController();
var jobController = TextEditingController();
@override
void initState() {
super.initState();
cards.add(createCard());
}
Card createCard() {
nameTECs.add(nameController);
ageTECs.add(ageController);
jobTECs.add(jobController);
return Card(
child: new Form(
key: _formKeys[_formKeys.length-1], // acess each form key here
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Person ${cards.length + 1}'),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: nameController,
decoration: InputDecoration(labelText: 'Full Name'),
validator: validatetext,
onSaved: (String val) {
person = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: ageController,
decoration: InputDecoration(labelText: 'Age'),
validator: validatetext,
onSaved: (String val) {
age = val;
},
),
TextFormField(
style: TextStyle(color: Colors.blue),
controller: jobController,
decoration: InputDecoration(labelText: 'Study/ job'),
validator: validatetext,
onSaved: (String val) {
job = val;
},
),
],
),
),
);
}
void _validateInputs() {
print('button');
for (int i = 0; i < _formKeys.length; i++) { // validate the form keys here
if (_formKeys[i].currentState.validate()) {
// validate each form
// If all data are correct then save data to out variables
_formKeys[i].currentState.save(); // dave each form
_onDone();
}
}
}
_onDone() {
List<PersonEntry> entries = [];
for (int i = 0; i < cards.length; i++) {
var name = nameTECs[i].text;
var age = ageTECs[i].text;
var job = jobTECs[i].text;
entries.add(PersonEntry(name, age, job));
}
Navigator.pop(context, entries);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: cards.length,
itemBuilder: (BuildContext context, int index) {
return cards[index];
},
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text('Add new'),
onPressed: () => setState(
() {
_formKeys.add(GlobalKey<FormState>()); // add a new form key
cards.add(createCard());
},
),
),
),
Padding(
padding: const EdgeInsets.all(16.0),
child: RaisedButton(
child: Text('Remove last'),
onPressed: () => setState(() {
cards.removeLast();
_formKeys.removeLast(); // remove the last form key
}),
),
)
],
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.save), onPressed: _validateInputs),
);
}
}
class PersonEntry {
final String name;
final String age;
final String studyJob;
PersonEntry(this.name, this.age, this.studyJob);
@override
String toString() {
return 'Person: name= $name, age= $age, study job= $studyJob';
}
}
String validatetext(String value) {
if (value.length < 5)
return 'More than 5 char is required';
else
return null;
}
结果:
注意:答案主要集中在解决 GlobalKey
的问题上,如果您键入 Form
,它将更新每个 Form
中的值>,因为您为 Forms
使用相同的 controllers
,因此您还可以通过创建 Controllers
的 List
来解决此问题>用于您的 TextFormFields
.
NOTE: The answer is mainly focused on solving the issue of the GlobalKey
, if you type in a Form
it updates value in every Form
because you are using the same controllers
for the Forms
, you can fix it by also creating a List
of Controllers
for your TextFormFields
.
这篇关于Flutter:多个小部件使用相同的GlobalKey或重复的GlobalKeys的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!