A Flutter application for displaying and filtering PamGene TIFF images from the Tercen platform with real-time TIFF to PNG conversion.
- Image Grid Display: Dynamic grid layout showing images with row numbers and column IDs
- Filter Controls:
- Cycle filter dropdown (dynamically populated from data)
- Exposure Time filter dropdown (dynamically populated from data)
- Tercen Integration: Real integration with Tercen platform API using sci_tercen_client
- TIFF Support: Client-side TIFF to PNG conversion using WebAssembly
- Zip Archive Support: Automatically extracts and displays TIFF images from zip files
- Lazy Loading: Metadata loaded on startup, images fetched on-demand
- Smart Caching: 50MB LRU cache for converted PNG bytes
- Theme Support: Light/dark mode toggle
- Clean Architecture: Service abstraction, dependency injection, and state management
This project follows clean architecture principles:
- Models: Abstract interfaces and concrete implementations
- Services: Abstract service interfaces for data operations
- Implementations: Mock service implementations (ready for real backend integration)
- Providers: State management using Provider pattern
- Screens: UI screens
- Widgets: Reusable UI components
- DI: Dependency injection using GetIt
- Flutter SDK (^3.10.1)
- Dart SDK
- Access to Tercen platform (for real data integration)
- Install dependencies:
flutter pub getRun with mock data for development and testing (requires USE_MOCKS=true):
# Web
flutter run -d chrome --dart-define=USE_MOCKS=true
# Desktop (Windows)
flutter run -d windows --dart-define=USE_MOCKS=trueRun with real Tercen data using explicit token and document ID for local testing:
Default Document ID: 9d8fdc8ec1d6f203834caa17f10038cb
For Windows PowerShell:
flutter run -d chrome `
--dart-define=USE_MOCKS=false `
--dart-define=DEV_TOKEN=<your-tercen-token> `
--dart-define=DEV_SERVER_URL=https://stage.tercen.com `
--dart-define=DEV_ZIP_FILE_ID=9d8fdc8ec1d6f203834caa17f10038cb `
--web-browser-flag="--disable-web-security" `
--web-browser-flag="--user-data-dir=C:\temp\chrome-dev-session"For Linux/Mac (bash/zsh):
flutter run -d chrome \
--dart-define=USE_MOCKS=false \
--dart-define=DEV_TOKEN=<your-tercen-token> \
--dart-define=DEV_SERVER_URL=https://stage.tercen.com \
--dart-define=DEV_ZIP_FILE_ID=9d8fdc8ec1d6f203834caa17f10038cb \
--web-browser-flag="--disable-web-security" \
--web-browser-flag="--user-data-dir=/tmp/chrome-dev-session"How to get your Tercen token:
- Log in to https://stage.tercen.com
- Open browser developer tools (F12)
- Go to Console tab
- Run:
localStorage.getItem('tercen.token') - Copy the token (without quotes)
Important: The
--disable-web-securityflag is needed for local development to bypass CORS restrictions. This is only for development - in production, the app will be deployed to a Tercen-allowed domain.
Override the default document ID with a custom one:
Windows PowerShell:
flutter run -d chrome --dart-define=USE_MOCKS=false --dart-define=TERCEN_TOKEN=<your-token> --dart-define=SERVICE_URI=https://stage.tercen.com --dart-define=DEV_ZIP_FILE_ID=<your-document-id>Linux/Mac:
flutter run -d chrome \
--dart-define=USE_MOCKS=false \
--dart-define=TERCEN_TOKEN=<your-token> \
--dart-define=SERVICE_URI=https://stage.tercen.com \
--dart-define=DEV_ZIP_FILE_ID=<your-document-id>Run in production with workflow and step IDs:
Windows PowerShell:
flutter run -d chrome --dart-define=USE_MOCKS=false --dart-define=TERCEN_TOKEN=<your-token> --dart-define=SERVICE_URI=https://tercen.com --dart-define=WORKFLOW_ID=<workflow-id> --dart-define=STEP_ID=<step-id>Linux/Mac:
flutter run -d chrome \
--dart-define=USE_MOCKS=false \
--dart-define=TERCEN_TOKEN=<your-token> \
--dart-define=SERVICE_URI=https://tercen.com \
--dart-define=WORKFLOW_ID=<workflow-id> \
--dart-define=STEP_ID=<step-id>lib/
├── di/
│ └── service_locator.dart # Dependency injection setup
├── models/
│ ├── image_metadata.dart # Abstract image metadata interface
│ ├── image_metadata_impl.dart # Concrete implementation
│ ├── image_collection.dart # Collection model
│ └── filter_criteria.dart # Filter criteria model
├── services/
│ └── image_service.dart # Abstract service interface
├── implementations/
│ └── services/
│ ├── mock_image_service.dart # Mock service implementation
│ └── tercen_image_service.dart # Real Tercen API implementation
├── providers/
│ ├── image_overview_provider.dart # State management
│ └── theme_provider.dart # Theme state management
├── screens/
│ └── image_overview_screen.dart # Main screen
├── widgets/
│ └── image_grid_cell.dart # Grid cell widget
├── utils/
│ ├── image_bytes_cache.dart # LRU cache for PNG bytes
│ └── tiff_converter.dart # TIFF to PNG conversion
└── main.dart # Application entry point
provider: ^6.1.1- State managementget_it: ^7.6.4- Service locator for dependency injectionimage: ^4.2.0- TIFF to PNG conversion (runtime)sci_tercen_client: 1.7.0- Official Tercen platform client library
-
Service Factory Pattern: Uses Tercen's ServiceFactory for API access
-
Three Operational Modes:
- Mock Mode: Uses hardcoded mock data (default)
- Development Mode: Uses hardcoded zip file document ID
- Production Mode: Queries files by workflow and step IDs
-
Data Flow:
Tercen Platform API ↓ (fetch metadata + TIFF bytes) TercenImageService ↓ (parse filenames, extract metadata) TiffConverter (WASM) ↓ (convert TIFF → PNG) ImageBytesCache (50MB LRU) ↓ (cached PNG bytes) UI Layer (ImageOverviewProvider)
- Startup: Loads image metadata only (fast)
- On-Demand: Downloads and converts TIFF images when displayed
- Caching: Stores converted PNG bytes (50MB max, LRU eviction)
The application automatically handles zip archives:
- Detects
.zipfiles from Tercen - Lists all entries in the zip using
listZipContents() - Extracts TIFF files individually using
downloadZipEntry() - No need to download entire zip file
| Variable | Description | Default | Example |
|---|---|---|---|
USE_MOCKS |
Enable mock data mode | true |
false |
DEV_TOKEN |
Tercen authentication token (local dev) | - | eyJ0eXAi... |
DEV_SERVER_URL |
Tercen server URL (local dev) | - | https://stage.tercen.com |
DEV_ZIP_FILE_ID |
Document ID for development | 9d8fdc8ec1d6f203834caa17f10038cb |
abc123... |
WORKFLOW_ID |
Production workflow ID | - | workflow123 |
STEP_ID |
Production step ID | - | step456 |
The default zip file document ID is hardcoded in lib/implementations/services/tercen_image_service.dart:46:
defaultValue: '9d8fdc8ec1d6f203834caa17f10038cb'You can change this directly in the code or override it with --dart-define=DEV_ZIP_FILE_ID=<id>.
Error: Access to XMLHttpRequest ... has been blocked by CORS policy
Cause: Web browsers block cross-origin requests from localhost to external domains for security.
Solution: Run Chrome with CORS disabled (development only):
flutter run -d chrome --dart-define=USE_MOCKS=false --dart-define=TERCEN_TOKEN=<your-token> --dart-define=SERVICE_URI=https://stage.tercen.com --web-browser-flag="--disable-web-security" --web-browser-flag="--user-data-dir=C:\temp\chrome-dev-session"Important Notes:
- Only use
--disable-web-securityfor local development - Close all Chrome instances before running this command
- In production (deployed to Tercen domain), CORS is not an issue
If you see authentication errors, your token may have expired. Generate a new token from Tercen and update the TERCEN_TOKEN environment variable.
If images fail to display:
- Check browser console for conversion errors
- Verify TIFF files are valid and accessible
- Check filename parsing in TiffConverter.parseFilename()
The cache is limited to 50MB. If you're loading many large images:
- Reduce cache size in lib/utils/image_bytes_cache.dart:21
- Clear cache manually using the service's
clearCache()method
- Image selection and export
- Zoom and detail views
- Advanced filtering (by row, column, etc.)
- Batch download functionality
- Image comparison mode
See FLUTTER_TECHNICAL_SPECIFICATION.md for detailed architecture and implementation guidelines.