3

I have a list view which has a card as a child of list view. The card contains a TextFormField with a form key. Since it is a listview it builds the card multiple times with the same form key so it is giving error:

Duplicate GlobalKey detected in widget tree.

Please help me with this.

This is my code.

  final GlobalKey<FormState> _formKey =
  new GlobalKey<FormState>(debugLabel: ' _formKey');

 @override
 Widget build(BuildContext context) {

return  Card(
    margin: EdgeInsets.symmetric(
      horizontal: 15,
      vertical: 4,
    ),
    child: Padding(
      padding: EdgeInsets.all(8),
      child: Column(
        children: <Widget>[
          ListTile(
            leading: CircleAvatar(
              backgroundColor: Colors.blue,
              child: Padding(
                padding: EdgeInsets.all(5),
                child: FittedBox(
                  child: Text(
                    widget.price,
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
            ),
            title: Text('Title'),
            subtitle: Text('Subtitle'),
            trailing: Text(widget.quantity.toString() + ' x'),
          ),

          Padding(
              padding: const EdgeInsets.only(
                  left: 32, right: 5, top: 0, bottom: 5),
              child: Container(
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    new Form(
                      key: this._formKey,
                      child: new TextFormField(
                        decoration: new InputDecoration(
                            hintText: 'Add your comments',
                            labelText: 'Comments'),
                        // validator: this._validateEmail,
                        onSaved: (String value) {
                          //  _formKey.currentState.save();

                        },
                      ),
                    ),

                  ],
                ),

              ))

  }
USER9561
  • 1,084
  • 3
  • 15
  • 41

2 Answers2

3

You can copy paste run full test code below
You can use List<GlobalKey<FormState>> and generate key via data list length
code snippet

  List<GlobalKey<FormState>> _formKey = [];
  ...
  @override
  void initState() {
    // TODO: implement initState
    _formKey = new List<GlobalKey<FormState>>.generate(dataList.length,
        (i) => new GlobalKey<FormState>(debugLabel: ' _formKey'));
    super.initState();
  }
  ...
  new Form(
            key: this._formKey[index],

working demo

enter image description here

full test code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        appBarTheme: AppBarTheme(color: Colors.green),
        primarySwatch: Colors.purple,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class Data {
  String price;
  int quantity;
  Data({this.price, this.quantity});
}

class _MyHomePageState extends State<MyHomePage> {
  List<Data> dataList = [
    Data(price: "1", quantity: 2),
    Data(price: "3", quantity: 4)
  ];
  List<GlobalKey<FormState>> _formKey = [];

  @override
  void initState() {
    // TODO: implement initState
    _formKey = new List<GlobalKey<FormState>>.generate(dataList.length,
        (i) => new GlobalKey<FormState>(debugLabel: ' _formKey'));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: AppBar(
          title: Text('Simple Card view'),
        ),
        body: new ListView.builder(
            padding: const EdgeInsets.all(16),
            itemCount: dataList.length,
            itemBuilder: (context, index) {
              return Card(
                  margin: EdgeInsets.symmetric(
                    horizontal: 15,
                    vertical: 4,
                  ),
                  child: Padding(
                      padding: EdgeInsets.all(8),
                      child: Column(children: <Widget>[
                        ListTile(
                          leading: CircleAvatar(
                            backgroundColor: Colors.blue,
                            child: Padding(
                              padding: EdgeInsets.all(5),
                              child: FittedBox(
                                child: Text(
                                  dataList[index].price,
                                  style: TextStyle(color: Colors.white),
                                ),
                              ),
                            ),
                          ),
                          title: Text('Title'),
                          subtitle: Text('Subtitle'),
                          trailing:
                              Text(dataList[index].quantity.toString() + ' x'),
                        ),
                        Padding(
                            padding: const EdgeInsets.only(
                                left: 32, right: 5, top: 0, bottom: 5),
                            child: Container(
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.start,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  new Form(
                                    key: this._formKey[index],
                                    child: new TextFormField(
                                      decoration: new InputDecoration(
                                          hintText: 'Add your comments',
                                          labelText: 'Comments'),
                                      // validator: this._validateEmail,
                                      onSaved: (String value) {
                                        //  _formKey.currentState.save();
                                      },
                                    ),
                                  ),
                                ],
                              ),
                            ))
                      ])));
            }));
  }
}
chunhunghan
  • 51,087
  • 5
  • 102
  • 120
1

For people wondering why given solution(s) are not working for them:

In my case I was using dependency injection, where I forgot to register (lazy) singletons to provide in my widgets.

So I was using GetIt with BLoC and did not register the bloc, event, state, etc.