Flutter:多个小部件使用相同的GlobalKey或重复的GlobalKeys [英] Flutter: Multiple widgets used the same GlobalKey or Duplicate GlobalKeys
问题描述
我正在尝试创建动态表单,并使用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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!