在同步调用中返回 Future 的结果
Posted
技术标签:
【中文标题】在同步调用中返回 Future 的结果【英文标题】:Returning the result of a Future in a synchronous call 【发布时间】:2020-12-17 18:50:29 【问题描述】:我正在使用 FlutterFormBuilder 包中的签名板来捕获签名(FlutterFormBuilderSignaturePad),将其上传到 firebase 存储,然后将下载 url 返回到应用程序以存储在 firestore 中的文档中。
我面临的问题是上传需要几秒钟才能完成(在连接不良时可能会更长)。我正在尝试等待电话,以便我可以将下载 url 传递给数据库,但它忽略了我的尝试。
我试过了:
使用.then()
和 .whenComplete()
链接我的调用,但 valueTransformer 仍然返回一个空白字符串。
将异步添加到“valueTransformer”、“onSaved”和“onChange”方法并等待调用
在上述三个方法之间移动了保存签名的逻辑,以便给 uimage 上传时间
onChanges 触发很多,所以我引入了一个 _processing 标志,因此它没有多次保存图像并导致数据库超时。 onChange 在几秒钟后返回一个 url,但我不能保证签名是完整的。
所以我的小部件看起来像这样:
final SignatureController _controller = SignatureController(
penStrokeWidth: 5,
penColor: Colors.red,
exportBackgroundColor: Colors.blue,
);
String _signature;
File _signatureFile;
bool _processing;
return FormBuilderSignaturePad(
name: 'signature',
controller: _controller,
decoration: InputDecoration(labelText: "signature"),
initialValue: _signatureFile?.readAsBytesSync(),
onSaved: (newValue) async
//called on save just before valueTransformer
await processSignature(newValue, context);
,
valueTransformer: (value)
//called when the form is saved
return _signature;
,
onChanged: (value)
//called frequently as the signature changes
if (_controller.isNotEmpty)
if (_controller.value.length > 19)
if (!_processing)
processSignature(value, context).then((value)
setState(()
_processing = false;
);
);
,
)
处理上传和设置状态的未来
Future<void> processSignature(dynamic signature, BuildContext context) async
setState(()
_processing = true;
);
var bytes = await _controller.toPngBytes();
final documentDirectory = await getApplicationDocumentsDirectory();
final file =
File(join(documentDirectory.path, 'signature$database.uid.png'));
file.writeAsBytesSync(bytes);
var url = await storage.uploadImage(
context: context,
imageToUpload: file,
title: "signature$database.uid.png",
requestId: database.currentRequest.id);
setState(()
_signature = url.imageUrl;
_signatureFile = file;
);
以下更改后的更新
进程签名:
Future<String> processSignature(
dynamic signature, BuildContext context) async
var bytes = await _controller.toPngBytes();
final documentDirectory = await getApplicationDocumentsDirectory();
final file =
File(join(documentDirectory.path, 'signature$database.uid.png'));
file.writeAsBytesSync(bytes);
var url = await storage.uploadImage(
context: context,
imageToUpload: file,
title: "signature$database.uid.png",
requestId: database.currentRequest.id);
return url.imageUrl;
签名板小部件:
return FormBuilderSignaturePad(
name: 'signature',
controller: _controller,
decoration: InputDecoration(labelText: "signature"),
initialValue: _signatureFile?.readAsBytesSync(),
onSaved: (newValue) async ,
valueTransformer: (value) async
final savedUrl = await processSignature(value, context);
return savedUrl;
,
onChanged: (value) ,
);
我看到“未来”的方法
_formKey[_currentStep].currentState.save();
if (_formKey[_currentStep].currentState.validate())
//request from the database
var request = firestoreDatabase.currentRequest;
//this should be the url however its returning as
//"Future<String>"
var value = _formKey[_currentStep].currentState.value;
request.questions[_currentStep].result =
jsonEncode(_formKey[_currentStep].currentState.value);
request.questions[_currentStep].completedOn =
Timestamp.fromDate(new DateTime.now());
firestoreDatabase.updateRequest(request).then((value)
if (_currentStep == _totalSteps - 1)
//pop the screen
Navigator.pop(context);
else
setState(()
_currentStep++;
);
【问题讨论】:
【参考方案1】:同步调用无法返回异步结果。 Future
表示它在未来的某个地方完成。
从onChanged
中删除processSignature
(为什么每次修改都发送签名?)并在onSaved
中处理。然后你可以使用 async/await 向服务器发送签名并等待结果 url。
class _SomeWidgetState extends State<SomeWidget>
/// Form key
final formKey = GlobalKey<FormState>();
/// Contains signature binary daya
Uint8List signatureValue;
@override
void build(...)
return Column(
children: [
FormBuilderSignaturePad(
...
onSaved(Uint8List value) async
signatureValue = value;
,
FlatButton(
child: Text('Submit'),
onPressed: ()
_submit();
),
],
);
/// Submits form
Future< void> _submit() async
if (formKey.currentState.validate())
formKey.currentState.save(); // calls all `onSaved` for each form widgets
// So at this point you have initialized `signatureValue`
try
final signatureUrl = await processSignature(signatureValue, context); // save into database
await doSomethingWithUrl(signatureUrl); // insert into document
on SomeExceptionIfRequired catch (e)
// Show error if occurred
ScaffoldMessenger.of(context).showSnackbar(...);
【讨论】:
我将 async 标签添加到 onsaved 并包含 await 但它在完成之前运行 valuetransfor 你应该将processSignature
的代码放在一个回调中,即final
在调用序列中。
我已将 processsignature 移至 valuetransformer,现在它返回一个字符串 "FutureprocessSignature
,让它返回Future<String>
,然后在valueTransformer
回调中调用它。请参阅我的答案中的 sn-p。以上是关于在同步调用中返回 Future 的结果的主要内容,如果未能解决你的问题,请参考以下文章