第二次重建 textfield_tag 小部件时,它会产生 A TextEditingController was used after being disposed 错误

When the textfield_tag widget is rebuilt for the second time, it produces the A TextEditingController was used after being disposed error

第二次重建 textfield_tag 小部件时,会产生 A TextEditingController was used after being disposed 错误。我正在显示基于切换开关的小部件。我试图将我在 initState() 方法中实例化的 TextFieldTagsController() 传递给小部件,但我得到相同的错误

在显示小部件之前

显示小部件后

现在我第二次切换开关后

这是代码

import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:marketplace/bloc/bloc.dart';
import 'package:marketplace/models/product.dart';
import 'package:image_picker/image_picker.dart';
import 'package:marketplace/screens/my_store_screen.dart';
import 'dart:io';
import 'package:textfield_tags/textfield_tags.dart';

import '../main.dart';
import '../utils/screen_utils.dart';

class AddProductScreenTwo extends StatefulWidget {
  static const routeName = '/add_product_screen_two';

  Product? _product;

  AddProductScreenTwo(this._product);

  @override
  _AddProductScreenTwoState createState() =>
      _AddProductScreenTwoState(_product);
}

class _AddProductScreenTwoState extends State<AddProductScreenTwo> {
  final _formKey = GlobalKey<FormState>();

  var bloc = getIt<Bloc>();

  late String _title, _description, _model, _sku, _price, _stock;
  String currencyDropDownValue = 'USD';
  String categoryDropDownValue = 'Shoes';
  String subcategoryDropDownValue = 'Sport';
  late String _colorDropDownValue, _sizeDropDownValue;
  List<String> currencies = [
    "USD",
    "THA",
  ];
  var categories = ['Shoes', 'Pants', 'Shirt', 'Smoking'];
  var _colorOptions = ['Red', 'Green', 'Yellow'];
  var _sizeOptions = ['L', 'M', 'S'];
  var subcategories = ['Sport', 'Casual'];
  List<XFile> _imageFileList = [];
  var _image;
  var imagePicker;

  bool _isUpdating = false;
  Product? _productToUpdate;
  int _deliveryMethodValue = 1;
  bool _hasVariant = false;

  // var _txtFieldTagController;

  _AddProductScreenTwoState(this._productToUpdate) {
    _colorDropDownValue = _colorOptions[0];
    _sizeDropDownValue = _sizeOptions[0];
  }

  @override
  void initState() {
    print("calling INITSTATE");
    // _txtFieldTagController = TextFieldTagsController();

    super.initState();
    imagePicker = new ImagePicker();
  }

  @override
  Widget build(BuildContext context) {
    ScreenUtils().init(context);
    return Scaffold(
      body: SafeArea(
        child: Form(
          key: _formKey,
          child: SingleChildScrollView(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              // mainAxisSize: MainAxisSize.min,
              children: [
                Padding(
                  padding: EdgeInsets.symmetric(
                    horizontal: getProportionateScreenWidth(16),
                  ),
                  child: Column(
                    // mainAxisSize: MainAxisSize.min,
                    // crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Add New Product',
                        style: Theme.of(context).textTheme.headline3!.copyWith(
                              fontWeight: FontWeight.bold,
                            ),
                      ), // Title : Add New Product
                      SizedBox(
                        height: getProportionateScreenHeight(20),
                      ),
                      TextFormField(
                        initialValue: _productToUpdate == null
                            ? ""
                            : _productToUpdate!.name.toString(),
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _title = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'Title',
                            helperText: 'eg. Long Sleeve tshirt',
                            // errorText: _titleError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // Title field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      TextFormField(
                        initialValue: _productToUpdate == null
                            ? ""
                            : _productToUpdate!.brand.toString(),
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _description = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'Description',
                            // errorText: _brandError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // Description Field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      Text("Categories"), // Category selector label
                      Container(
                        padding: const EdgeInsets.all(6.0),
                        width: MediaQuery.of(context).size.width,
                        decoration: BoxDecoration(
                            border: Border.all(color: Colors.grey),
                            borderRadius: BorderRadius.circular(0.0)),
                        child: DropdownButtonHideUnderline(
                          child: DropdownButton<String>(
                            isExpanded: true,
                            value: categoryDropDownValue,
                            icon: const Icon(Icons.arrow_drop_down),
                            iconSize: 32,
                            elevation: 16,
                            style: const TextStyle(
                                fontSize: 20, color: Colors.black54),
                            onChanged: (String? newValue) {
                              setState(() {
                                categoryDropDownValue = newValue ?? "";
                              });
                            },
                            items: categories
                                .map<DropdownMenuItem<String>>((String value) {
                              return DropdownMenuItem<String>(
                                value: value,
                                child: Text(value),
                              );
                            }).toList(),
                          ),
                        ),
                      ), // Category selector field
                      SizedBox(
                        height: getProportionateScreenHeight(25),
                      ),
                      Text("Images"), // Image selector label
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      _imageFileList.length != 0
                          ? SizedBox(
                              height: 200,
                              child: ListView.builder(
                                scrollDirection: Axis.horizontal,
                                itemBuilder: (BuildContext context, int index) {
                                  return SizedBox(
                                    height: 200,
                                    width: 200,
                                    child: Padding(
                                      padding: const EdgeInsets.all(8.0),
                                      child: Stack(children: [
                                        Image.file(
                                            File(_imageFileList[index].path)),
                                        Positioned(
                                          bottom: 5.0,
                                          right: 5.0,
                                          child: IconButton(
                                              onPressed: () => removeImage(
                                                  _imageFileList[index]),
                                              icon: Icon(
                                                Icons.delete_forever,
                                                color: Colors.red,
                                              )),
                                        ),
                                      ]),
                                    ),
                                  );
                                },
                                itemCount: _imageFileList.length,
                              ),
                            )
                          : Text("no image"),
                      IconButton(
                          onPressed: () => pickMultipleImages(),
                          icon: Icon(Icons.add_a_photo_outlined)),
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      TextFormField(
                        initialValue: _productToUpdate == null
                            ? ""
                            : _productToUpdate!.price.toString(),
                        keyboardType: TextInputType.number,
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _price = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'Price',
                            // errorText: _priceError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // Price Field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      TextFormField(
                        initialValue: _productToUpdate == null
                            ? ""
                            : _productToUpdate!.sku.toString(),
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _sku = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'SKU',
                            // errorText: _skuError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // SKU Field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      TextFormField(
                        keyboardType: TextInputType.number,
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _stock = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'Stock Quantity',
                            // errorText: _stockError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // Stock Quantity field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      Text("Shipping methods"), // Delivery options label
                      ListTile(
                          title: Text("Home Delivery"),
                          leading: Radio<int>(
                            value: 1,
                            groupValue: _deliveryMethodValue,
                            onChanged: (value) {
                              if (value != null) {
                                setState(() {
                                  _deliveryMethodValue = value;
                                });
                              }
                            },
                          )), // Home delivery option
                      ListTile(
                          title: Text("Pick up in store"),
                          leading: Radio<int>(
                            value: 2,
                            groupValue: _deliveryMethodValue,
                            onChanged: (value) {
                              if (value != null) {
                                setState(() {
                                  _deliveryMethodValue = value;
                                });
                              }
                            },
                          )), // Pick up in store option
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      TextFormField(
                        keyboardType: TextInputType.number,
                        initialValue: _productToUpdate == null
                            ? ""
                            : _productToUpdate!.sku.toString(),
                        cursorColor: Theme.of(context).cursorColor,
                        validator: (text) => cannotBeEmptyValidator(text),
                        onChanged: (value) {
                          _sku = value;
                        },
                        decoration: InputDecoration(
                            labelText: 'Weight in KG',
                            // errorText: _skuError,
                            border: OutlineInputBorder(
                                borderRadius: const BorderRadius.all(
                                    Radius.circular(0.0)))),
                      ), // Weight Field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      Text("Variants"), // Variants label
                      Container(
                        padding: const EdgeInsets.all(6.0),
                        decoration: BoxDecoration(
                            border: Border.all(color: Colors.grey),
                            borderRadius: BorderRadius.circular(0.0)),
                        child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Text(
                                "Product has variant",
                                style: Theme.of(context)
                                    .textTheme
                                    .bodyText1!
                                    .copyWith(
                                      fontWeight: FontWeight.bold,
                                    ),
                              ),
                              Switch(
                                  value: _hasVariant,
                                  onChanged: (val) {
                                    setState(() {
                                      _hasVariant = val;
                                    });
                                  })
                            ]),
                      ), // Variants field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      _hasVariant
                          ? TextFieldTags(
                              textSeparators: [
                                " ", //seperate with space
                                ',' //sepearate with comma as well
                              ],
                              initialTags: _colorOptions,
                              onTag: (tag) => _colorOptions.add(tag),
                              onDelete: (tag) => _colorOptions.remove(tag),
                              tagsStyler: TagsStyler(
                                  //styling tag style
                                  tagTextStyle:
                                      TextStyle(fontWeight: FontWeight.normal),
                                  tagDecoration: BoxDecoration(
                                    color: Colors.blue[100],
                                    borderRadius: BorderRadius.circular(0.0),
                                  ),
                                  tagCancelIcon: Icon(Icons.cancel,
                                      size: 18.0, color: Colors.blue[900]),
                                  tagPadding: EdgeInsets.all(6.0)),
                              textFieldStyler: TextFieldStyler(
                                  //styling tag text field
                                  textFieldBorder: OutlineInputBorder(
                                      borderSide: BorderSide(
                                          color: Colors.blue, width: 2))),
                              // textFieldTagsController: _txtFieldTagController,
                            )
                          : Container(),

                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      Container(
                        padding: const EdgeInsets.all(6.0),
                        decoration: BoxDecoration(
                            border: Border.all(color: Colors.grey),
                            borderRadius: BorderRadius.circular(0.0)),
                        child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Text(
                                "Show to customers",
                                style: Theme.of(context)
                                    .textTheme
                                    .bodyText1!
                                    .copyWith(
                                      fontWeight: FontWeight.bold,
                                    ),
                              ),
                              Switch(
                                  value: true,
                                  onChanged: (val) {
                                    //TODO update the product visibility value online
                                    // setState(() {
                                    //   switchValue = val;
                                    // });
                                  })
                            ]),
                      ), // Availability field
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      StreamBuilder<bool>(
                          stream: bloc.isPostingProduct,
                          builder: (context, snapshot) {
                            print("SNAPSHOT: ${snapshot.data}");

                            if (snapshot.data == true) {
                              // Product is being posted
                              return CircularProgressIndicator();
                            }
                            return Container();
                          }),
                      SizedBox(
                        height: getProportionateScreenHeight(15),
                      ),
                      ElevatedButton(
                        onPressed: () {
                          _submit();
                        },
                        child: Text('ADD PRODUCT',
                            style: TextStyle(fontWeight: FontWeight.bold)),
                      ), // Add product button
                    ],
                  ),
                )
              ],
            ),
          ),
        ),
      ),
    );
  }


  Future pickMultipleImages() async {
    try {
      var images = await ImagePicker().pickMultiImage();
      setState(() {
        _imageFileList.addAll(images!);
      });
      for (var image in _imageFileList) {
        print("Multiple images picked : " + image.path);
      }
    } catch (e) {
      print("Error : $e");
    }
  }

  void removeImage(XFile imageFile) {
    setState(() {
      _imageFileList.remove(imageFile);
    });
  }

  void _submit() async {
    //TODO
    // validate all the form fields
    if (_formKey.currentState!.validate()) {
      ProductTwo product = ProductTwo(
        title: _title,
        description: _description,
        sku: _sku,
        price: double.parse(_price),
      );

      print("POSTING PRODUCT : $product");
      var result = await bloc.postProduct(product);
      if (result) {
        const snackBar = SnackBar(
          content: Text('Your product was saved successfully'),
        );

        ScaffoldMessenger.of(context).showSnackBar(snackBar);

        Navigator.of(context).pushReplacementNamed(MyStoreScreen.routeName);
      }
    }
  }

  _showPostSuccessDialog() {
    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          content: Container(
            height: 120,
            child: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  Icon(Icons.done, size: 64, color: Colors.green),
                  SizedBox(
                    height: getProportionateScreenHeight(20),
                  ),
                  Text(
                    "Your product was successfully saved",
                    style: TextStyle(color: Colors.green),
                  ),
                ]),
          ),
        );
      },
    );
  }

  String? cannotBeEmptyValidator(String? text) {
    if (text == null || text.isEmpty) {
      return 'Can\'t be empty';
    }
    return null;
  }
}

尝试添加“mounted”以检查小部件是否未处理。

_hasVariant && mounted
                          ? TextFieldTags(
                        textSeparators: [
                          " ", //seperate with space
                          ',' //sepearate with comma as well
                        ],
                        initialTags: _colorOptions,
                        onTag: (tag) => _colorOptions.add(tag),
                        onDelete: (tag) => _colorOptions.remove(tag),
                        tagsStyler: TagsStyler(
                          //styling tag style
                            tagTextStyle:
                            TextStyle(fontWeight: FontWeight.normal),
                            tagDecoration: BoxDecoration(
                              color: Colors.blue[100],
                              borderRadius: BorderRadius.circular(0.0),
                            ),
                            tagCancelIcon: Icon(Icons.cancel,
                                size: 18.0, color: Colors.blue[900]),
                            tagPadding: EdgeInsets.all(6.0)),
                        textFieldStyler: TextFieldStyler(
                          //styling tag text field
                            textFieldBorder: OutlineInputBorder(
                                borderSide: BorderSide(
                                    color: Colors.blue, width: 2))),
                        // textFieldTagsController: _txtFieldTagController,
                      )
                          : Container(),

这是 textfield_tags 库 1.4.4 版中的错误。它目前在静态字段中存储 TextEditingControllerFocusNode 并为 TextFieldTags.

的每个实例重复使用它们

您唯一能做的就是在 tracker 上提交问题并降级到 1.4.3,直到问题在更高版本中得到解决。