uploadFile method

  1. @override
Future<UploadResult> uploadFile(
  1. String resourceKey,
  2. String field, {
  3. required List<int> bytes,
  4. required String filename,
})
override

POST /{resource}/upload — uploads a file for a single-file upload field. field is the field's statePath, so a nested field is addressable.

Fails with UploadFailed rather than throwing, including when the host's transport has not implemented the optional FilamentUploadTransport port — the same never-throw contract every other write has.

Implementation

@override
Future<UploadResult> uploadFile(
  String resourceKey,
  String field, {
  required List<int> bytes,
  required String filename,
}) async {
  // Capability is detected, not assumed: a host that never implemented
  // the optional upload port gets an actionable message, not a crash or
  // a silent no-op — this is also the signal Task 6's form field reads
  // to stay read-only.
  //
  // `FilamentUploadTransport` is a sibling interface, not a subtype of
  // `FilamentTransport`, so Dart cannot promote `_transport` from the
  // `is!` check below — the explicit cast is required, not decorative.
  if (_transport is! FilamentUploadTransport) {
    return const UploadFailed(
      'This host transport does not implement FilamentUploadTransport, '
      'so files cannot be uploaded. Implement it alongside '
      'FilamentTransport to enable this field.',
    );
  }
  final transport = _transport as FilamentUploadTransport;

  try {
    final response = await transport.upload(
      '$prefix/$resourceKey/upload',
      bytes: bytes,
      filename: filename,
      field: field,
    );

    if (response.statusCode >= 200 && response.statusCode < 300) {
      final path = response.body['path'];
      return path is String
          ? UploadSuccess(path)
          : UploadFailed(
              'Upload succeeded but the server sent no path.',
              statusCode: response.statusCode,
            );
    }

    final message = response.body['message'];
    return UploadFailed(
      message is String ? message : null,
      statusCode: response.statusCode,
    );
  } catch (e) {
    // Same contract as create/update/destroy: the transport throws on
    // socket/DNS/timeout, and an offline upload must come back as a
    // failed result, never an unhandled async error.
    return UploadFailed(messageOf(e));
  }
}