【问题标题】:Flutter: Multiple widgets used the same GlobalKey or Duplicate GlobalKeysFlutter:多个小部件使用相同的 GlobalKey 或 Duplicate GlobalKeys
【发布时间】:2020-12-31 18:40:05
【问题描述】:

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

下面是给出错误的代码 多个小部件使用相同的 GlobalKey 或 Duplicate Global 键。 我不确定如何解决此问题或如何按照标准清洁动态表单。

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> 

【问题讨论】:

    标签: flutter


    【解决方案1】:

    问题是您使用相同的密钥 _formKey 时为所有 forms。您可以创建一个 List_formKeys,其中包含 Globalkey&lt;FormState&gt; 并根据卡片的长度对其添加或删除密钥。

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

    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 ,您还可以通过为您的TextFormFields 创建一个ControllersList 来修复它。


    【讨论】:

      【解决方案2】:

      您在创建卡片并将其添加到列表卡片时使用相同的密钥 _formKey,您应该为每张卡片创建一个全局密钥作为相同大小卡片的列表,因此每次添加/删除一个卡你对全局密钥列表做同样的事情

      【讨论】:

      • 感谢您的回答。你能给我举个例子吗。所以,我可以更好地理解它。
      猜你喜欢
      • 2020-10-01
      • 2023-03-04
      • 2018-09-26
      • 2019-06-07
      • 1970-01-01
      • 2021-08-26
      • 2019-06-01
      • 2021-10-13
      • 2021-07-30
      相关资源
      最近更新 更多