【问题标题】:Returning the result of a Future in a synchronous call在同步调用中返回 Future 的结果
【发布时间】: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++;
                          });
                        }

【问题讨论】:

    标签: flutter dart flutter-form-builder


    【解决方案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,现在它返回一个字符串 "Future" 而不是 value 。所有电话都在等待中
    • 把你的中间代码放在这里或其他地方。
    • 重构你的processSignature,让它返回Future&lt;String&gt;,然后在valueTransformer回调中调用它。请参阅我的答案中的 sn-p。
    猜你喜欢
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多