#flutter #dart #flutter-form-builder
#flutter #dart #flutter-конструктор форм
Вопрос:
Я использую панель подписи в пакете 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
означает, что он завершается где-то в будущем.
Удалить processSignature
из onChanged
(зачем отправлять подпись каждый раз при ее изменении?) и обработайте его 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(...);
}
}
}
}
Комментарии:
1. Я добавил тег async в onsaved и включил ожидание, но перед завершением он запускает valuetransfor
2. Вы должны поместить код
processSignature
в обратный вызов, который находитсяfinal
в последовательности вызовов.3. Я переместил processsignature в valuetransformer, и теперь он возвращает строку «Future<string>», а не значение . Ожидаются все вызовы
4. Поместите свой промежуточный код здесь или где-нибудь.
5. Реорганизуйте ваш
processSignature
, чтобы он возвращалсяFuture<String>
, затем вызовите его вvalueTransformer
обратном вызове. Смотрите Фрагмент в моем ответе.