submit method
Validates client-side first, then writes. Returns true only when saved.
A 401 here is not mapped to isUnauthenticated: a transport failure
on write already lands in WriteFailed and shows through formError,
same as any other save failure, and a token that expired mid-edit is a
save the user can retry from the same screen once signed back in — it
is not the "there is no form to show" case isUnauthenticated exists
for. Same scope choice Task 5 made for loadMore().
Implementation
Future<bool> submit() async {
if (_submitting) return false;
_formError = null;
_fieldErrors = Map.unmodifiable(validate(_components, _values, strings));
// A hint may only delay a submission by a round-trip; it never reaches the
// server, so nothing here can forbid what the server would accept.
if (_fieldErrors.isNotEmpty) {
notifyListeners();
return false;
}
_submitting = true;
notifyListeners();
final payload = _values.payloadFor(_components);
WriteResult result;
try {
final target = submitTarget;
// One branch, two URL families: the relation target changes WHERE the
// payload goes, never what it is — validation, the 422 mapping and the
// banner below are shared verbatim, because the server keys a relation
// write's 422 by the same child-form field names this screen renders.
result = target == null
? recordId == null
? await source.create(resource.key, payload)
: await source.update(resource.key, recordId!, payload)
: recordId == null
? await source.createRelation(
target.resourceKey,
target.recordId,
target.relation,
payload,
)
: await source.updateRelation(
target.resourceKey,
target.recordId,
target.relation,
recordId!,
payload,
);
} catch (e) {
// create/update return their 4xx as data; only a transport failure
// throws, and its message is already fit for the user.
result = WriteFailed(messageOf(e));
}
_submitting = false;
switch (result) {
case WriteSuccess(:final data):
_savedRecordId = data[resource.recordKey];
_notify();
return true;
case WriteInvalid(:final errors):
_applyServerErrors(errors);
case WriteDenied(:final message) ||
WriteGone(:final message) ||
WriteFailed(:final message):
_formError = message.isEmpty ? strings.saveFailed : message;
}
_notify();
return false;
}