When the textfield_tag widget is rebuilt for the second time, it produces the A TextEditingController was used after being disposed error. I am displaying the widget based on the toggle switch. I have tried to pass to the widget an TextFieldTagsController() that I instantiate in the initState() method but i get the same error
BEFORE SHOWING THE WIDGET
AFTER SHOWING THE WIDGET
NOW AFTER I TOGGLE THE SWITCH FOR THE SECOND TIME
HERE IS THE CODE
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;
}
}
CodePudding user response:
Try to add "mounted" to check if the widget is not dispose.
_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(),
CodePudding user response:
This is a bug in version 1.4.4 of the textfield_tags library. It currently stores a TextEditingController and FocusNode in static fields and reuses them for every instance of TextFieldTags.
The only thing you can do is file an issue on the tracker and downgrade to 1.4.3 until the problem is fixed in a later version.



