bottleneckcrm/mobile/lib/widgets/common/attach_file_button.dart
Ashwin eea82484e2 feat: add file attachment functionality to task and ticket detail screens
- Implemented file attachment upload feature in task detail screen.
- Added a new AttachFileButton widget for initiating file uploads.
- Created AttachmentTile widget to display uploaded files with icons based on file type.
- Enhanced ticket detail screen to include a new Files tab for viewing and uploading attachments.
- Introduced AttachmentUploadResult class to handle the result of file uploads.
- Added postMultipart method in ApiService for handling multipart file uploads.
- Implemented attachment upload logic in attachment_upload.dart service.
- Updated pubspec.yaml to include file_picker dependency for file selection.
- Added unit tests for attachment upload functionality to ensure reliability.
2026-08-07 11:03:06 +05:30

44 lines
1.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart';
import '../../core/theme/theme.dart';
/// "Attach a file", on every detail screen that lists attachments.
///
/// Disabled while an upload is in flight rather than hidden, so the row does
/// not jump and a second tap cannot start a second upload of the same file.
class AttachFileButton extends StatelessWidget {
const AttachFileButton({
super.key,
required this.onPressed,
this.isUploading = false,
this.label = 'Attach a file',
});
final VoidCallback onPressed;
final bool isUploading;
final String label;
@override
Widget build(BuildContext context) {
return TextButton.icon(
onPressed: isUploading ? null : onPressed,
icon: isUploading
? const SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(LucideIcons.paperclip, size: 16),
label: Text(isUploading ? 'Uploading…' : label),
style: TextButton.styleFrom(
foregroundColor: AppColors.primary600,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
// A themed button's minimum size is infinite width inside a Row, which
// has blanked a page here before. Shrink-wrap instead.
minimumSize: Size.zero,
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
),
);
}
}