Flutter:多个小部件使用相同的GlobalKey或重复的GlobalKeys

时间:2020-09-14 03:03:51

标签: flutter

我正在尝试创建动态表单,并使用TextFormField进行验证。

下面是给出错误的代码。多个小部件使用相同的GlobalKey或Duplicate Global Key。 我不确定如何解决此问题或如何使Dynamic Form符合标准。

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;
  }

万一有人想要完整的错误。

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> 

2 个答案:

答案 0 :(得分:1)

问题是,当您为所有_formKey使用相同的密钥forms时。您可以创建一个List的{​​{1}},其中包含_formKeys,并根据卡的长度在其中添加或删除密钥。

我以您的代码为例添加了一个演示:

Globalkey<FormState>

结果: result


注意:答案主要集中在解决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中的值,因为您使用的是相同的{{ 1}} Form,也可以通过为controllers创建Forms的{​​{1}}来解决此问题。


答案 1 :(得分:0)

在创建卡片并将其添加到列表卡片时,您使用相同的密钥_formKey,您应该为每张卡片创建一个全局密钥,以作为卡片大小相同的列表,因此每次添加/删除卡片时卡,您也要对全局密钥列表进行相同操作