无法从 Flutter 向 Firebase 实时数据库发送 post 请求
Can't send post requests from Flutter to Firebase Realtime Database
我是一名 flutter 初学者,我正在 Udemy 学习“Flutter & Dart - 完整指南 [2022 版]”课程(如果你知道我在第 10 节第 242 课“发送 POST 请求”。这是我第一次使用 Firebase 和数据库,我在这个问题上停留了很长一段时间。我们在前面的部分构建了一个基本的商店应用程序,现在我们应该发送一个 Post 当我们向我们的应用程序添加新产品时请求我们的 Firebase 实时数据库。我们应该向我们的数据库添加一些关于产品的基本信息。当我尝试添加新产品时,没有任何反应,我的数据库中没有数据,也没有新的我的应用程序中的产品(在发送 Post 请求后立即以相同的功能向应用程序添加产品)。我的终端没有错误,只有下面一行。
I/flutter (11699): ...
在添加 http 包和编写上一课的 post 请求代码之前,我能够将产品添加到应用程序。这些是我的调试、数据库规则和发送 post 请求的函数。
void addProduct(Product product) {
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products.json');
http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
final newProduct = Product(
id: DateTime.now().toString(),
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl);
_items.add(newProduct);
notifyListeners();
}
我尝试了课程构造器示例代码,但没有成功。然后我尝试禁用我的防火墙,将我的数据库规则更改为我的问题中所见,仍然是同样的问题。我什至不知道我的代码是否正在与 firebase 通信。
编辑:
有时我也会在我的终端上获得最新的文本,并在下面添加屏幕截图:
Click to see Terminal SS
编辑 2 : 所以我创建了一个空项目来查看我的计算机是否正在与 firebase 通信并且它有效,我从一个单屏应用程序向我的数据库添加了信息.恐怕我的商店应用程序中有一个逻辑错误(很可能在屏幕上我使用了 addProduct 函数)所以我正在 post 输入代码,如果您看到错误请警告我。这是 edit-product-screen,我在其中通过提供商使用 addProduct 功能。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/product.dart';
import '../providers/product_provider.dart';
class EditProductScreen extends StatefulWidget {
static const String routeName = '/edit-product-screen';
@override
State<EditProductScreen> createState() => _EditProductScreenState();
}
class _EditProductScreenState extends State<EditProductScreen> {
//final _priceFocusNode = FocusNode();
//final _descriptionFocusNode = FocusNode();
final _imageUrlController = TextEditingController();
//final _imageUrlFocusNode = FocusNode();
final _form = GlobalKey<FormState>();
var _editedProduct =
Product(id: '', title: '', description: '', price: 0, imageUrl: '');
var _isInit = true;
var _isloading = false;
var _initValues = {
'title': '',
'description': '',
'price': '',
'imageUrl': '',
};
@override
void initState() {
//_imageUrlFocusNode.addListener(_updateImageUrl);
super.initState();
}
@override
void didChangeDependencies() {
if (_isInit) {
final String? productId =
ModalRoute.of(context)!.settings.arguments as String?;
if (productId != null) {
_editedProduct = Provider.of<ProductProvider>(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 dispose() {
// _imageUrlFocusNode.removeListener(_updateImageUrl);
// _priceFocusNode.dispose();
// _descriptionFocusNode.dispose();
// _imageUrlController.dispose();
// _imageUrlFocusNode.dispose();
// super.dispose();
// }
void _updateImageUrl() {
// if (!_imageUrlFocusNode.hasFocus) {
// if ((!_imageUrlController.text.startsWith('http') &&
// !_imageUrlController.text.startsWith('https')) ||
// (!_imageUrlController.text.endsWith('.png') &&
// !_imageUrlController.text.endsWith('.jpg') &&
// !_imageUrlController.text.endsWith('.jpeg'))) {
// return;
// }
setState(() {});
//}
}
void _saveForm() {
final isValid = _form.currentState!.validate();
if (!isValid) {
return;
}
_form.currentState!.save();
setState(() {
_isloading = true;
});
if (_editedProduct.id != null) {
//var olan bir product editleniyor
Provider.of<ProductProvider>(context, listen: false)
.updateProduct(_editedProduct.id, _editedProduct);
setState(() {
_isloading = false;
});
Navigator.of(context).pop();
} else {
//yeni bir product oluşturuluyor.
Provider.of<ProductProvider>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog<Null>(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An Error Occured'),
content: Text(error.toString()),
actions: [
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text('Okay'))
],
),
);
}).then((_) {
setState(() {
_isloading = false;
});
Navigator.of(context).pop();
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Products'),
actions: [
IconButton(
onPressed: _saveForm,
icon: Icon(Icons.save),
),
],
),
body: _isloading
? Center(
child: CircularProgressIndicator(),
)
: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _form,
child: ListView(
children: [
TextFormField(
initialValue: _initValues['title'],
decoration: InputDecoration(labelText: 'Title'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a title';
}
return null;
},
textInputAction: TextInputAction.next,
// onFieldSubmitted: (_) {
// FocusScope.of(context).requestFocus(_priceFocusNode);
// },
onSaved: (value) {
_editedProduct = Product(
title: value as String,
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
TextFormField(
initialValue: _initValues['price'],
decoration: InputDecoration(labelText: 'Price'),
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
//focusNode: _priceFocusNode,
// onFieldSubmitted: (_) {
// FocusScope.of(context).requestFocus(_descriptionFocusNode);
// },
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a price';
}
if (double.tryParse(value) == null) {
return 'Please enter a valid number';
}
if (double.parse(value) <= 0) {
return 'Plase enter a number greater than zero';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: double.parse(value as String),
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
TextFormField(
initialValue: _initValues['description'],
decoration: InputDecoration(labelText: 'Description'),
maxLines: 3,
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a description';
}
if (value.length < 10) {
return 'Your description must be longer then 10 characters';
}
return null;
},
textInputAction: TextInputAction.next,
//focusNode: _descriptionFocusNode,
keyboardType: TextInputType.multiline,
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: value as String,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
width: 100,
height: 100,
margin: EdgeInsets.only(top: 8, right: 10),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey),
),
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'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a image url';
}
if (!value.startsWith('http') &&
!value.startsWith('https')) {
return 'Please enter a valid image url';
}
return null;
},
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller: _imageUrlController,
//focusNode: _imageUrlFocusNode,
onFieldSubmitted: (_) {
_saveForm();
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: value as String,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
),
],
)
],
),
),
),
);
}
}
这是最新版本的 addProduct 函数
Future<void> addProduct(Product product) async {
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products2.json');
try {
final response = await http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
print(json.decode(response.body));
final newProduct = Product(
id: DateTime.now().toString(),
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl);
_items.add(newProduct);
notifyListeners();
} catch (error) {
print(error);
throw error;
}
}
我认为您没有实现 await。在尝试更改您的方法 Future 之后,您能否尝试在开头添加 await http.post 然后它会起作用。
Future<Product> addProduct(Product product) async{
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products.json');
await http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
最后,您应该使用 json to dart side 进行解析,并尝试使用工厂方法对您的模型实施 json 编码。 (我已经将我的第一个 post 添加到您的后端,请不要忘记为每个人更改写入规则)
如果您想了解更多详情,请查看dart.pad
样品:
我是一名 flutter 初学者,我正在 Udemy 学习“Flutter & Dart - 完整指南 [2022 版]”课程(如果你知道我在第 10 节第 242 课“发送 POST 请求”。这是我第一次使用 Firebase 和数据库,我在这个问题上停留了很长一段时间。我们在前面的部分构建了一个基本的商店应用程序,现在我们应该发送一个 Post 当我们向我们的应用程序添加新产品时请求我们的 Firebase 实时数据库。我们应该向我们的数据库添加一些关于产品的基本信息。当我尝试添加新产品时,没有任何反应,我的数据库中没有数据,也没有新的我的应用程序中的产品(在发送 Post 请求后立即以相同的功能向应用程序添加产品)。我的终端没有错误,只有下面一行。
I/flutter (11699): ...
在添加 http 包和编写上一课的 post 请求代码之前,我能够将产品添加到应用程序。这些是我的调试、数据库规则和发送 post 请求的函数。
void addProduct(Product product) {
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products.json');
http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
final newProduct = Product(
id: DateTime.now().toString(),
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl);
_items.add(newProduct);
notifyListeners();
}
我尝试了课程构造器示例代码,但没有成功。然后我尝试禁用我的防火墙,将我的数据库规则更改为我的问题中所见,仍然是同样的问题。我什至不知道我的代码是否正在与 firebase 通信。
编辑: 有时我也会在我的终端上获得最新的文本,并在下面添加屏幕截图:
Click to see Terminal SS
编辑 2 : 所以我创建了一个空项目来查看我的计算机是否正在与 firebase 通信并且它有效,我从一个单屏应用程序向我的数据库添加了信息.恐怕我的商店应用程序中有一个逻辑错误(很可能在屏幕上我使用了 addProduct 函数)所以我正在 post 输入代码,如果您看到错误请警告我。这是 edit-product-screen,我在其中通过提供商使用 addProduct 功能。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/product.dart';
import '../providers/product_provider.dart';
class EditProductScreen extends StatefulWidget {
static const String routeName = '/edit-product-screen';
@override
State<EditProductScreen> createState() => _EditProductScreenState();
}
class _EditProductScreenState extends State<EditProductScreen> {
//final _priceFocusNode = FocusNode();
//final _descriptionFocusNode = FocusNode();
final _imageUrlController = TextEditingController();
//final _imageUrlFocusNode = FocusNode();
final _form = GlobalKey<FormState>();
var _editedProduct =
Product(id: '', title: '', description: '', price: 0, imageUrl: '');
var _isInit = true;
var _isloading = false;
var _initValues = {
'title': '',
'description': '',
'price': '',
'imageUrl': '',
};
@override
void initState() {
//_imageUrlFocusNode.addListener(_updateImageUrl);
super.initState();
}
@override
void didChangeDependencies() {
if (_isInit) {
final String? productId =
ModalRoute.of(context)!.settings.arguments as String?;
if (productId != null) {
_editedProduct = Provider.of<ProductProvider>(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 dispose() {
// _imageUrlFocusNode.removeListener(_updateImageUrl);
// _priceFocusNode.dispose();
// _descriptionFocusNode.dispose();
// _imageUrlController.dispose();
// _imageUrlFocusNode.dispose();
// super.dispose();
// }
void _updateImageUrl() {
// if (!_imageUrlFocusNode.hasFocus) {
// if ((!_imageUrlController.text.startsWith('http') &&
// !_imageUrlController.text.startsWith('https')) ||
// (!_imageUrlController.text.endsWith('.png') &&
// !_imageUrlController.text.endsWith('.jpg') &&
// !_imageUrlController.text.endsWith('.jpeg'))) {
// return;
// }
setState(() {});
//}
}
void _saveForm() {
final isValid = _form.currentState!.validate();
if (!isValid) {
return;
}
_form.currentState!.save();
setState(() {
_isloading = true;
});
if (_editedProduct.id != null) {
//var olan bir product editleniyor
Provider.of<ProductProvider>(context, listen: false)
.updateProduct(_editedProduct.id, _editedProduct);
setState(() {
_isloading = false;
});
Navigator.of(context).pop();
} else {
//yeni bir product oluşturuluyor.
Provider.of<ProductProvider>(context, listen: false)
.addProduct(_editedProduct)
.catchError((error) {
return showDialog<Null>(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An Error Occured'),
content: Text(error.toString()),
actions: [
FlatButton(
onPressed: () {
Navigator.of(ctx).pop();
},
child: Text('Okay'))
],
),
);
}).then((_) {
setState(() {
_isloading = false;
});
Navigator.of(context).pop();
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Products'),
actions: [
IconButton(
onPressed: _saveForm,
icon: Icon(Icons.save),
),
],
),
body: _isloading
? Center(
child: CircularProgressIndicator(),
)
: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _form,
child: ListView(
children: [
TextFormField(
initialValue: _initValues['title'],
decoration: InputDecoration(labelText: 'Title'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a title';
}
return null;
},
textInputAction: TextInputAction.next,
// onFieldSubmitted: (_) {
// FocusScope.of(context).requestFocus(_priceFocusNode);
// },
onSaved: (value) {
_editedProduct = Product(
title: value as String,
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
TextFormField(
initialValue: _initValues['price'],
decoration: InputDecoration(labelText: 'Price'),
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
//focusNode: _priceFocusNode,
// onFieldSubmitted: (_) {
// FocusScope.of(context).requestFocus(_descriptionFocusNode);
// },
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a price';
}
if (double.tryParse(value) == null) {
return 'Please enter a valid number';
}
if (double.parse(value) <= 0) {
return 'Plase enter a number greater than zero';
}
return null;
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: double.parse(value as String),
description: _editedProduct.description,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
TextFormField(
initialValue: _initValues['description'],
decoration: InputDecoration(labelText: 'Description'),
maxLines: 3,
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a description';
}
if (value.length < 10) {
return 'Your description must be longer then 10 characters';
}
return null;
},
textInputAction: TextInputAction.next,
//focusNode: _descriptionFocusNode,
keyboardType: TextInputType.multiline,
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: value as String,
imageUrl: _editedProduct.imageUrl,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
width: 100,
height: 100,
margin: EdgeInsets.only(top: 8, right: 10),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey),
),
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'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter a image url';
}
if (!value.startsWith('http') &&
!value.startsWith('https')) {
return 'Please enter a valid image url';
}
return null;
},
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller: _imageUrlController,
//focusNode: _imageUrlFocusNode,
onFieldSubmitted: (_) {
_saveForm();
},
onSaved: (value) {
_editedProduct = Product(
title: _editedProduct.title,
price: _editedProduct.price,
description: _editedProduct.description,
imageUrl: value as String,
id: _editedProduct.id,
isFavorite: _editedProduct.isFavorite,
);
},
),
),
],
)
],
),
),
),
);
}
}
这是最新版本的 addProduct 函数
Future<void> addProduct(Product product) async {
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products2.json');
try {
final response = await http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
print(json.decode(response.body));
final newProduct = Product(
id: DateTime.now().toString(),
title: product.title,
description: product.description,
price: product.price,
imageUrl: product.imageUrl);
_items.add(newProduct);
notifyListeners();
} catch (error) {
print(error);
throw error;
}
}
我认为您没有实现 await。在尝试更改您的方法 Future 之后,您能否尝试在开头添加 await http.post 然后它会起作用。
Future<Product> addProduct(Product product) async{
final url = Uri.parse(
'https://flutter-update-ca55a-default-rtdb.firebaseio.com/products.json');
await http.post(
url,
body: json.encode({
'title': product.title,
'description': product.description,
'imageUrl': product.imageUrl,
'price': product.price,
'isFavorite': product.isFavorite,
}),
);
最后,您应该使用 json to dart side 进行解析,并尝试使用工厂方法对您的模型实施 json 编码。 (我已经将我的第一个 post 添加到您的后端,请不要忘记为每个人更改写入规则)
如果您想了解更多详情,请查看dart.pad 样品: