EditProductScreen用于更新现有产品或添加新产品,唯一的区别是更新产品屏幕数据时填充现有产品的初始值以更新它。应用程序正常工作以更新产品,但当我单击按钮添加新产品时,抛出标题中提到的错误。
我使用didChangeDependencies来获取现有产品的ID,并设置其初始值。由于新产品还没有ID,如果条件应用于它,但程序仍然抛出错误。
编辑产品屏幕
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/product.dart';
import '../providers/products.dart';
class EditProductScreen extends StatefulWidget {
static const routeName = '/edit-product-screen';
// As form filling will going to take place here we need to show changes on page
@override
State<EditProductScreen> createState() => _EditProductScreenState();
}
class _EditProductScreenState extends State<EditProductScreen> {
final _priceFocusNode = FocusNode();
final _descriptionFocusNode = FocusNode();
final _imageUrlController = TextEditingController();
final _imageUrlFocusNode = FocusNode();
// FocusNode stick in memory and use much storage (here we are using stateful widget not Provider), so we need to dispose them
final _form = GlobalKey<FormState>();
var _editedProduct = Product(
id: '',
title: '',
description: '',
price: 0,
imageUrl: '',
);
var _isInit = true;
var _initValues = {
'title': '',
'description': '',
'price': '',
'imageUrl': '',
};
@override
void dispose() {
_imageUrlFocusNode.removeListener(
_updateImageUrl); // dispose before diaposing _imageUrlFocusNode
_priceFocusNode.dispose();
_descriptionFocusNode.dispose();
_imageUrlController.dispose();
_imageUrlFocusNode.dispose();
super.dispose();
}
@override
void didChangeDependencies() {
if (_isInit) {
final productId = ModalRoute.of(context)!.settings.arguments as String;
// print('Siddhant her is your Id $productId');
if (productId != null) {
// if (!productId.isEmpty)
_editedProduct =
Provider.of<Products>(context, listen: false).findById(productId);
_initValues = {
'title': _editedProduct.title,
'description': _editedProduct.description,
'price': _editedProduct.price.toString(),
// 'imageUrl': _editedProduct.imageUrl,
'imageUrl': '',
};
_imageUrlController.text = _editedProduct.imageUrl;
}
}
_isInit = false;
super.didChangeDependencies();
}
@override
void initState() {
_imageUrlFocusNode.addListener(
_updateImageUrl); // execute updateImageUrl whenever imageUrlFocusNode chandeg
super.initState();
}
void _updateImageUrl() {
if (!_imageUrlFocusNode.hasFocus) {
if ((_imageUrlController.text.isEmpty) ||
(!_imageUrlController.text.startsWith('http') &&
!_imageUrlController.text.startsWith('https')) ||
(!_imageUrlController.text.endsWith('jpeg') &&
!_imageUrlController.text.endsWith('png') &&
!_imageUrlController.text.endsWith('jpg'))) {
return;
}
setState(() {});
}
}
void _saveForm() {
final _isValid = _form.currentState!.validate(); // trigger all validators
if (!_isValid) {
return;
}
_form.currentState!.save();
if (_editedProduct.id != null) {
Provider.of<Products>(context, listen: false)
.updateProduct(_editedProduct.id, _editedProduct);
} else {
Provider.of<Products>(context, listen: false).addProduct(_editedProduct);
}
Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Products'),
actions: [
// TextButton(
// onPressed: () {
// Navigator.of(context).pop();
// },
// child: Text(
// 'Cancle',
// style: TextStyle(
// color: Colors.red,
// fontSize: 17,
// ),
// ),
// ),
IconButton(
onPressed: _saveForm,
icon: Icon(Icons.save),
),
],
),
body: Padding(
padding: EdgeInsets.all(16),
child: Form(
key: _form,
child: ListView(
children: [
TextFormField(
initialValue: _initValues['title'],
decoration: InputDecoration(labelText: 'Title'),
textInputAction: TextInputAction.next,
validator: (value) {
if (value!.isEmpty) {
return 'Please provide a title';
}
return null;
},
onFieldSubmitted: (_) {
FocusScope.of(context).requestFocus(_priceFocusNode);
},
onSaved: (newValue) {
_editedProduct = Product(
id: _editedProduct.id,
title: newValue.toString(),
description: _editedProduct.description,
price: _editedProduct.price,
imageUrl: _editedProduct.imageUrl,
isFavourite: _editedProduct.isFavourite,
);
},
),
TextFormField(
initialValue: _initValues['price'],
decoration: InputDecoration(labelText: 'Price'),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
focusNode: _priceFocusNode,
validator: (value) {
if (value!.isEmpty) {
return 'Please Enter a Price.';
}
if (double.tryParse(value) == null) {
// tryParse returns a null when it fails
return 'Please Enter a Valid Price.';
}
if (double.parse(value) < 0) {
return 'Price can not be Negative.';
}
return null;
},
onFieldSubmitted: (_) {
FocusScope.of(context).requestFocus(_descriptionFocusNode);
},
onSaved: (newValue) {
_editedProduct = Product(
id: _editedProduct.id,
title: _editedProduct.title,
description: _editedProduct.description,
price: double.parse(newValue.toString()),
imageUrl: _editedProduct.imageUrl,
isFavourite: _editedProduct.isFavourite,
);
},
),
TextFormField(
initialValue: _initValues['description'],
decoration: InputDecoration(labelText: 'Description'),
maxLines: 3,
keyboardType: TextInputType.multiline,
focusNode: _descriptionFocusNode,
validator: (value) {
if (value!.isEmpty) {
return 'Please Enter a Description.';
}
if (value.length < 10) {
return 'Should be at least 10 characters long.';
}
return null;
},
onSaved: (newValue) {
_editedProduct = Product(
id: _editedProduct.id,
title: _editedProduct.title,
description: newValue.toString(),
price: _editedProduct.price,
imageUrl: _editedProduct.imageUrl,
isFavourite: _editedProduct.isFavourite,
);
},
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Container(
height: 100,
width: 100,
margin: EdgeInsets.only(top: 8, right: 10),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.black87),
),
child: _imageUrlController.text.isEmpty
? Text('Enter a URL')
: FittedBox(
child: Image.network(
_imageUrlController.text,
fit: BoxFit.cover,
),
),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(labelText: 'Image URL'),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller:
_imageUrlController, // TextFormField already has a controller but here in case of imageUrl we need our own controller to preview the image
focusNode: _imageUrlFocusNode,
validator: (value) {
if (value!.isEmpty) {
return 'Please enter an URL';
}
if (!value.startsWith('http') &&
!value.startsWith('https')) {
return 'Enter a valid URL';
}
if (!value.endsWith('jpeg') &&
!value.endsWith('png') &&
!value.endsWith('jpg')) {
return 'Enter a valid Image URL';
}
return null;
},
onSaved: (newValue) {
_editedProduct = Product(
id: _editedProduct.id,
title: _editedProduct.title,
description: _editedProduct.description,
price: _editedProduct.price,
imageUrl: newValue.toString(),
isFavourite: _editedProduct.isFavourite,
);
},
// onFieldSubmitted: (_) {
// _saveForm();
// },
),
),
],
),
],
),
),
),
);
}
}
用户产品项目
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../screens/edit_product_screen.dart';
import '../providers/products.dart';
class UserProductItem extends StatelessWidget {
final String id;
final String title;
final String imageUrl;
UserProductItem(this.id, this.title, this.imageUrl);
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(title),
leading: CircleAvatar(
backgroundImage: NetworkImage(imageUrl),
),
trailing: Container(
width: 100,
child: Row(children: [
IconButton(
onPressed: () {
Navigator.of(context)
.pushNamed(EditProductScreen.routeName, arguments: id);
// print(id);
},
icon: Icon(Icons.edit),
color: Theme.of(context).primaryColor,
),
IconButton(
onPressed: () {
Provider.of<Products>(context, listen: false).deleteProduct(id);
},
icon: Icon(Icons.delete),
color: Theme.of(context).errorColor,
),
]),
),
);
}
}
2条答案
按热度按时间lsmepo6l1#
您可以考虑在创建产品时使用此方法,如果参数的类型为int,则将"“更改为0
5n0oy7gb2#
错误可能发生于
与其使用
as
,不如接受空值。现在,在使用它时,执行空检查,如