This file is for Claude Code and other LLM/AI coding agents.
CRITICAL REQUIREMENT FOR ALL AI AGENTS: Before making ANY code changes, planning, or implementation in this codebase, you MUST read the complete documentation to understand Fluppy's architecture and alignment goals.
READ THESE FILES IMMEDIATELY:
doc/uppy-study.md- Uppy architecture reference (MOST IMPORTANT)doc/plan.md- How to create implementation plansdoc/implement.md- How to implement approved plansdoc/research.md- How to research the codebaseREADME.md- Package overview and usage
This is NOT optional. This is a MANDATORY requirement.
Fluppy is a Flutter/Dart package for file uploads, inspired by Uppy.js.
- Platform: Dart package (works with Flutter, CLI, server-side Dart)
- Goal: Achieve 1:1 feature parity with Uppy.js
- Architecture: Plugin-based modular architecture with abstract uploaders
- Key Technologies: Dart, Streams, Sealed Classes, Abstract Classes
- Uppy Alignment: Fluppy aims to replicate Uppy's API, architecture, and patterns
- Dart-Idiomatic: Use Dart/Flutter best practices (Streams, sealed classes, etc.)
- Modular: Plugin-based architecture with swappable uploaders
- Headless: No UI included (bring your own Flutter widgets)
- Backend-Agnostic: Works with any upload backend through uploader implementations
# Get dependencies
dart pub get
# Run tests
dart test
# Run specific test file
dart test test/fluppy_test.dart
# Code formatting
dart format .
# Static analysis
dart analyze
# Run example
dart run example/example.dartlib/
├── fluppy.dart # Public API exports
└── src/
├── core/ # Core framework
│ ├── fluppy.dart # Main orchestrator
│ ├── uploader.dart # Abstract uploader base
│ ├── fluppy_file.dart # File model
│ └── events.dart # Event system (sealed classes)
└── [uploader]/ # Uploader implementations
├── s3/ # AWS S3 uploader
├── tus/ # Tus resumable uploads (planned)
└── http/ # HTTP/XHR uploader (planned)
test/ # Unit and integration tests
example/ # Usage examples
doc/ # Documentation and guides
Before implementing ANY feature:
- Read
doc/uppy-study.mdto understand Uppy's approach - Match API naming conventions with Uppy
- Replicate event names and lifecycle hooks
- Document any deviations from Uppy (with reasoning)
Example - Uppy has addFile(), Fluppy should have addFile() (not addNewFile() or insertFile())
- Streams instead of EventEmitter (Dart-idiomatic)
- Sealed classes for type-safe events
- Abstract classes for extensibility
- async/await for asynchronous operations
- Extension methods for utility functions
- Null safety properly enforced
- dartdoc comments on all public APIs
- Keep public API minimal (export from
lib/fluppy.dartonly) - Implementation details in
lib/src/(not exported) - Semantic versioning (breaking changes = major version)
- Backwards compatibility when possible
- Unit tests for all public APIs
- Integration tests for complete workflows
- Mock uploaders for testing without network
- Example app demonstrates real usage
- Aim for high coverage (>80%)
- dartdoc comments on all public APIs
- Code examples in documentation
- README.md kept current with features
- CHANGELOG.md updated with every change
- Reference Uppy docs where relevant
- Read
doc/uppy-study.md- Understand Uppy's implementation - Create a plan - Follow
doc/plan.mdprocess - Get approval - Wait for user confirmation
- Implement - Follow
doc/implement.mdprocess with approved plan - Test - Write comprehensive tests
- Document - Update README, dartdocs, examples
- Update changelog - Document changes
- Understand the issue - Read relevant code and tests
- Check Uppy behavior - Ensure alignment with Uppy
- Fix the bug - Minimal changes
- Add test - Prevent regression
- Update changelog - Document fix
- Verify need - Ensure improvement is valuable
- Plan changes - Use
doc/plan.mdif complex - Maintain API compatibility - Don't break existing users
- Test thoroughly - Ensure no behavior changes
- Document - Explain reasoning
Fluppy (lib/src/core/fluppy.dart)
- Main orchestrator class
- Manages files, uploaders, and lifecycle
- Emits events via Streams
- Public API:
addFile(),upload(),pauseAll(), etc.
Uploader (lib/src/core/uploader.dart)
- Abstract base class for all uploaders
- Defines upload contract
- Implementations:
S3Uploader,TusUploader(planned), etc.
FluppyFile (lib/src/core/fluppy_file.dart)
- Represents a file to be uploaded
- Supports multiple sources: path, bytes, stream
- Tracks status, progress, metadata
FluppyEvent (lib/src/core/events.dart)
- Sealed class hierarchy for type-safe events
- Events:
FileAdded,UploadProgress,UploadComplete, etc. - Emitted via
fluppy.eventsstream
┌─────────────────────────────────────────┐
│ User Code (Flutter App) │
└───────────────┬─────────────────────────┘
│
│ addFile(), upload()
▼
┌─────────────────────────────────────────┐
│ Fluppy (Core Orchestrator) │
│ - File management │
│ - Event emission │
│ - Lifecycle control │
└───────────────┬─────────────────────────┘
│
│ upload() call
▼
┌─────────────────────────────────────────┐
│ Uploader (S3Uploader, TusUploader) │
│ - Protocol-specific upload logic │
│ - Progress tracking │
│ - Error handling │
└───────────────┬─────────────────────────┘
│
│ HTTP requests
▼
┌─────────────────────────────────────────┐
│ Backend (S3, Tus server, etc.) │
└─────────────────────────────────────────┘
Fluppy uses Streams and Sealed Classes for events:
// Listen to all events
fluppy.events.listen((event) {
switch (event) {
case FileAdded(:final file):
print('File added: ${file.name}');
case UploadProgress(:final fileId, :final bytesUploaded, :final bytesTotal):
print('Progress: $bytesUploaded / $bytesTotal');
case UploadComplete(:final fileId, :final response):
print('Upload complete: ${response.url}');
case UploadError(:final fileId, :final error):
print('Error: $error');
}
});| Uppy Feature | Fluppy Status | Implementation |
|---|---|---|
| Core orchestrator | ✅ Complete | lib/src/core/fluppy.dart |
| Event system | ✅ Complete | lib/src/core/events.dart |
| File management | ✅ Complete | lib/src/core/fluppy_file.dart |
| S3 uploader (single) | ✅ Complete | lib/src/s3/s3_uploader.dart |
| S3 uploader (multipart) | ✅ Complete | lib/src/s3/s3_uploader.dart |
| AWS Signature V4 | ✅ Complete | lib/src/s3/aws_signature_v4.dart |
| Pause/Resume | ✅ Complete | Core + S3 |
| Retry logic | ✅ Complete | Core |
| Progress tracking | ✅ Complete | Core + Uploaders |
| Tus uploader | ❌ Missing | Planned |
| XHR/HTTP uploader | ❌ Missing | Planned |
| Preprocessing pipeline | ❌ Missing | Planned |
| Postprocessing pipeline | ❌ Missing | Planned |
| File restrictions | ❌ Missing | Planned |
| Remote sources | ❌ Missing | Out of scope? |
| UI components | ❌ Missing | Intentionally excluded |
- Preprocessing/Postprocessing - Core to Uppy's architecture
- Tus Uploader - Most requested resumable upload protocol
- File Restrictions - Validation (size, type, count)
- HTTP/XHR Uploader - Basic uploader for simple backends
- Create directory:
lib/src/[uploader_name]/ - Extend
Uploaderabstract class - Create options class (e.g.,
TusOptions) - Create types/models (e.g.,
TusTypes) - Implement
upload()method - Emit progress events
- Handle pause/resume/cancel
- Write tests
- Update README and examples
- Modify
lib/src/core/fluppy.dart - Add new events to
lib/src/core/events.dartif needed - Ensure API matches Uppy convention
- Write unit tests
- Update integration tests
- Update README
- Update
doc/uppy-study.mdif documenting gap
- Check semver impact - Is this a breaking change?
- Consider backwards compatibility - Can we avoid breaking?
- Update all affected code - Core, uploaders, tests, examples
- Update documentation - README, dartdocs
- Update CHANGELOG - Note breaking changes clearly
Test individual classes and methods in isolation:
test('addFile adds file to collection', () {
final fluppy = Fluppy(uploader: MockUploader());
final file = FluppyFile(/* ... */);
fluppy.addFile(file);
expect(fluppy.files, contains(file));
});Test complete workflows:
test('upload completes successfully', () async {
final fluppy = Fluppy(uploader: MockUploader());
fluppy.addFile(file);
final events = <FluppyEvent>[];
fluppy.events.listen(events.add);
await fluppy.upload();
expect(events, contains(isA<UploadComplete>()));
});Use mocks for testing without network:
class MockUploader extends Uploader {
@override
Future<UploadResponse> upload(FluppyFile file, CancellationToken token) async {
// Simulate upload
await Future.delayed(Duration(milliseconds: 100));
return UploadResponse(url: 'https://mock.url/file.jpg');
}
}All public APIs must have dartdoc comments:
/// Adds a file to the upload queue.
///
/// The [file] will be validated against any configured restrictions
/// before being added. If validation fails, an error event will be emitted.
///
/// Example:
/// ```dart
/// final file = FluppyFile(
/// name: 'photo.jpg',
/// source: FileSourceType.path,
/// path: '/path/to/photo.jpg',
/// );
/// fluppy.addFile(file);
/// ```
///
/// See also:
/// - [addFiles] for adding multiple files at once
/// - [removeFile] for removing files from the queue
void addFile(FluppyFile file) {
// Implementation
}Keep README current with:
- Feature list
- Installation instructions
- Quick start example
- API overview
- Link to full documentation
Follow Keep a Changelog format:
## [0.2.0] - 2026-01-15
### Added
- Tus uploader support for resumable uploads
- File size and type restrictions
### Changed
- Improved error messages for S3 uploads
### Fixed
- Multipart upload resume issue with large filesUppy uses EventEmitter (Node.js style):
uppy.on('upload-progress', (file, progress) => { })Fluppy uses Streams (Dart style):
fluppy.events.listen((event) {
if (event is UploadProgress) { }
})Use sealed classes for type-safe event handling:
sealed class FluppyEvent {}
class UploadProgress extends FluppyEvent {
final String fileId;
final int bytesUploaded;
final int bytesTotal;
}Benefits:
- Exhaustive switch statements
- Compile-time safety
- Pattern matching
Dart's dart:io is async by nature:
// Read file
final file = File(path);
final bytes = await file.readAsBytes();
// Stream file
final stream = file.openRead();Fluppy should work on:
- Flutter mobile (iOS, Android)
- Flutter web (with dart:html bridge)
- Flutter desktop (Windows, Mac, Linux)
- Dart CLI/server (with dart:io)
Consider platform differences when implementing features.
doc/uppy-study.md- Comprehensive Uppy referencedoc/plan.md- Implementation planning guidedoc/implement.md- Implementation execution guidedoc/research.md- Codebase research guideREADME.md- Package overview
If you encounter issues or have questions:
- Read the docs - Start with
doc/uppy-study.md - Check existing code - Look at S3Uploader as reference
- Ask the user - When design decisions are needed
- Document your findings - Use
doc/research.mdprocess
Remember:
- ✅ Always read
doc/uppy-study.mdbefore implementing features - ✅ Match Uppy's API and naming conventions
- ✅ Use Dart best practices (Streams, sealed classes)
- ✅ Write comprehensive tests
- ✅ Document everything (dartdocs, README, CHANGELOG)
- ✅ Follow the plan → implement → test → document workflow
Goal: Build a Dart package that's as good as Uppy, but Dart-native!