本文介绍了Flutter:多个小部件使用相同的GlobalKey或重复的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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-24 15:24