多个小部件在 flutter 中使用了相同的 globalkey 错误

Kys*_*M60 3 dart flutter

我试图解决这个问题,我在堆栈溢出中查找了答案

但是我还没解决

我在创建和更新页面中使用了全局键

我所做的

  1. 我尝试向全局密钥添加静态,但我不能,因为我无法将密钥包装在 refreshIndicator 中。

  2. 我使用 Navigator pushNamed 而不是 Navigator push

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class Update extends StatefulWidget {
  @override
  _UpdateState createState() => _UpdateState();
}

class _UpdateState extends State<Update> {
  GlobalKey<FormState> _formKey1 = GlobalKey<FormState>(debugLabel: '_updateFormKey');

  TextEditingController _titleController1 = TextEditingController();
  TextEditingController _descController1 = TextEditingController();
  final db = Firestore.instance;
  DocumentSnapshot _currentDocument;


  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    return MaterialApp(
        home: Scaffold(
            resizeToAvoidBottomInset: false,
            appBar: AppBar(
              title: Text('update'),
            ),
            body: _buildUpdate(context)));
  }

  Widget _buildUpdate(BuildContext context) {
    final Size size = MediaQuery.of(context).size;


    return StreamBuilder<QuerySnapshot>(
      stream: db.collection('flutter_data2').snapshots(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Column(
            children: snapshot.data.documents.map<Widget>((doc) {
              return Column(
                children: <Widget>[
                  Padding(
                    padding: EdgeInsets.all(20.0),
                    child: Card(
                      elevation: 2.0,
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(16.0)),
                      child: Form(
                        key: _formKey1,
                        child: Padding(
                          padding: EdgeInsets.only(left: 12, right: 12),
                          child: Column(
                            children: <Widget>[
                              TextFormField(
                                controller: _titleController1,
                                decoration: InputDecoration(labelText: doc.data['title']),
                                validator: (String value) {
                                  if (value.isEmpty) {
                                    return 'title empty';
                                  } else {
                                    return null;
                                  }
                                },
                              ),
                              TextFormField(
                                controller: _descController1,
                                decoration: InputDecoration(labelText: doc.data['desc']),
                                validator: (String value) {
                                  if (value.isEmpty) {
                                    return 'desc empty';
                                  } else {
                                    return null;
                                  }
                                },
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                  ),
                  RaisedButton(
                    shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(15.0)),
                    child: Text('update'),
                    color: Colors.blue,
                    onPressed: () async {
                      if (_formKey1.currentState.validate()) {
                        db
                            .collection('flutter_data2')
                            .document(doc.documentID)
                            .updateData({'title': _titleController1.text,'desc':_descController1.text});
                        Navigator.pop(context);
                      }
                    },
                  ),
                ],
              );
            }).toList(),
          );
        } else {
          return SizedBox();
        }
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

Afr*_*yal 7

您可能真的想在这里使用一些模块化。最好在具有自己的一组控制器的不同文件中创建您的自定义表单小部件。这样您就不必显式管理控制器。需要注意的另一件事是您的 Button 对每个条目都执行相同的工作。在这种情况下,您不妨在自定义 Form 小部件中添加全局键,并在那里对 onPressed 函数进行硬编码。

这是一个例子

// This is a mock data. Your firebase snapshot.data will have a similar structure
List<Map<String, dynamic>> _mockData = [
  {
    'title':'Title 1',
    'desc':'Description 1',
  },
  {
    'title':'Title 2',
    'desc':'Description 2',
  },
  {
    'title':'Title 3',
    'desc':'Description 3',
  },
  {
    'title':'Title 4',
    'desc':'Description 4',
  },
];

// There are many ways to make this work.
// Instead of hardcoding the function in our custom form widget, We would like to pass a function implementation which will be called after the button in the form is pressed. This way we will have more control on what will happen when we press the button
typedef onFormData = Future<void> Function(String, String); // Future void to allow async updates // The two strings are title and description respectively.


// This is the custom form widget you need to create
class MyForm extends StatefulWidget {
  final Map<String, dynamic> data; // Replace it with DocumentSnapshot data.
  final onFormData onPressed; // We will use the type we defined up there. So we will be expecting a function implementation here which takes two strings, a title and a description

  MyForm({@required this.data, @required this.onPressed, Key key}):super(key: key);

  @override
  createState() => _MyFormState();
}

// Our custom form widget is defined here
class _MyFormState extends State<MyForm> {

  // Define the controllers
  TextEditingController _titleController; 
  TextEditingController _descController;

  // Create the key
  GlobalKey<FormState> _formKey;

  @override
  void initState() {
    // Initialize the values here
    super.initState();
    _titleController = TextEditingController();
    _descController = TextEditingController();
    _formKey = GlobalKey<FormState>();
  }

  @override
  void dispose() {
    // Remember that you have to dispose of the controllers once the widget is ready to be disposed of
    _titleController.dispose();
    _descController.dispose();
    _formKey = null;
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // Everything remains almost same here as in your code
    return Column(
      children: <Widget>[
        Padding(
          padding: EdgeInsets.all(20.0),
          child: Card(
            elevation: 2.0,
            shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(16.0)),
            child: Form(
              key: _formKey,
              child: Padding(
                padding: EdgeInsets.only(left: 12, right: 12),
                child: Column(
                  children: <Widget>[
                    TextFormField(
                      controller: _titleController, // Assign the controller
                      decoration:
                          InputDecoration(labelText: widget.data['title']), // widget.data can still be indexed like this after you replace datatype of the data to DocumentSnapshot
                      validator: (String value) {
                        if (value.isEmpty) {
                          return 'title is empty';
                        } else {
                          return null;
                        }
                      },
                    ),
                    TextFormField(
                      controller: _descController,
                      decoration:
                          InputDecoration(labelText: widget.data['desc']), // Same goes here
                      validator: (String value) {
                        if (value.isEmpty) {
                          return 'description is empty';
                        } else {
                          return null;
                        }
                      },
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
        // The button associated with this form
        RaisedButton(
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)),
          child: Text('Update'),
          color: Colors.blue,
          onPressed: () async {
            // If validation is successful, then call the on pressed function we assigned to the widget. // Check the MyWidget class
            if (_formKey.currentState.validate()) {
              await widget.onPressed(_titleController.text, _descController.text); // Instead of putting firebase update code here, we are passing the title and description to our main widget from where we will post
            }
          },
        ),
      ],
    );
  }
}

// Our main widget
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Demo'),
      ),
      // Wrap this up in your stream builder
      // I am using a listview with mock data for the sake of this example.
      body: ListView.builder(
      itemBuilder: (context, index) {
        // We create a new instance of our custom form and we don't need to manage any controllers or keys. We just need to pass the data and what happens when we press the update button in our custom form.
        // Here is why we defined a type named onFormData before.
        // You can simply post updates in your form widget directly if your logic is same for each form
        // We are getting the title and description info here through our custom defined Forms without managing any keys and controllers.
        // Also this method is async so you can post your firebase updates from here waiting for them to complete using await
        return MyForm(data: _mockData[index], onPressed: (String title, String description) async {
          // Put your firebase update code here
          _mockData[index]['title'] = title;
          _mockData[index]['desc'] = description;
          Navigator.of(context).pop(); // Go back after the updates are made as written in your example
        });
      },
      physics: BouncingScrollPhysics(),
      itemCount: _mockData.length, // Length of the data.
        ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在任何更新之前:

更新前

写好标题和描述后:

写下你的标题和描述

按更新后,当您返回同一屏幕时:

按更新后,当您返回屏幕时

希望这可以帮助!