attachment_engine library

Universal Attachment Management Engine.

A single reusable Flutter plugin for resolving, caching, downloading, detecting, rendering, previewing, sharing and managing attachments of every common type (image, pdf, video, audio, html, text, office, archive, scorm, and more). See README.md for architecture and usage.

Classes

ArchiveAttachmentRenderer
Inspects a zip archive's contents and routes to SCORM/H5P handling, or offers a safe-extraction / external-open fallback for a generic zip.
Attachment
Core value type representing any piece of content the engine can resolve, cache, render, download or share.
AttachmentActions
Row of action buttons (share/download/open externally/delete cache) wired to an attachment's AttachmentCapabilities, so unavailable actions are simply omitted rather than shown disabled.
AttachmentCacheManager
Manages on-disk caching of attachment content, keyed by the stable logical identity of an attachment (Attachment.stableIdentity) - never by remote URL, which may be a rotating signed URL.
AttachmentCapabilities
Set of actions available for a given attachment, derived by CapabilityEngine from its type, status and source.
AttachmentDiagnosticsSink
Safe internal logging/telemetry hook. A host app can implement this to bridge events into Crashlytics, Sentry, or its own analytics.
AttachmentDownloadProgress
Displays download progress for an attachment as a linear progress bar, listening to a DownloadManager.progressStream.
AttachmentEngineConfig
Immutable, composable configuration for the whole attachment engine.
AttachmentErrorView
Maps an AttachmentFailure to a friendly error UI with a retry button.
AttachmentFailure
Typed, exhaustive set of failures the engine can surface. Each failure carries a simple default English message via localizedMessage which a host app can override globally through AttachmentLocalizations.
AttachmentGrid
A grid of attachment thumbnails, suitable for image-heavy attachment collections.
AttachmentList
A vertical list of AttachmentTiles.
AttachmentLocalizations
Hook allowing a host app to override the default English failure messages, e.g. by wiring in flutter_localizations / intl.
AttachmentManager
Top-level facade for the engine. Construct via the default constructor with injected collaborators (for testability), or use AttachmentManager.instance for a ready-to-use singleton in an app.
AttachmentMetadataStore
Abstract persistence interface for cache metadata. A host app that already has its own database set up should implement this against its own storage instead of using FileBasedMetadataStore.
AttachmentNotFound
AttachmentPreview
Lightweight, non-interactive preview of an attachment. Deliberately never instantiates a full renderer/controller (no video/audio/pdf controllers) - it is meant for list/card contexts where many previews may be on-screen at once.
AttachmentRenderer
A pluggable full-view renderer for a specific AttachmentType.
AttachmentResolver
Orchestrates resolving an Attachment to a usable local file: validate -> already local? -> cached? -> network available? -> download -> ResolvedAttachment.
AttachmentSource
Describes where the bytes of an Attachment originate from.
AttachmentThumbnail
Small square thumbnail for an attachment, used inside AttachmentTile and AttachmentGrid. Falls back to a type icon when no local image is available.
AttachmentTile
A single row representing an Attachment in a list, showing a thumbnail, name, and a state-dependent trailing widget (spinner while loading, error icon on failure, or a chevron when ready).
AttachmentViewer
Full-screen(-capable) attachment viewer. Routes to the appropriate AttachmentRenderer via RendererRegistry based on the attachment's detected type.
AudioAttachmentRenderer
AudioPlayerPool
Shared pool of NativeAudioControllers keyed by source so the same track isn't loaded into multiple concurrent players, and players are disposed exactly once (ref-counted, mirrors VideoControllerPool).
BytesAttachmentSource
CacheAttachmentSource
CacheConfig
Controls whether, and how, attachment content is cached on disk.
CacheEntry
Metadata about a single cached attachment file.
CacheFailed
CachePolicy
Retention rules for the attachment cache: a maximum total size with least-recently-used eviction, plus explicit clear operations.
CapabilityEngine
Derives the set of available actions (AttachmentCapabilities) for an attachment given its type, current status and source.
CircularProgressIndicatorPlaceholder
Minimal loading placeholder to avoid pulling in Material just for a spinner.
ConnectivityChecker
Hook for checking network reachability. Default implementation performs a lightweight DNS lookup, swallowing any error into false.
ConversionFailed
CorruptedFile
CsvAttachmentRenderer
Full-view CSV/TSV renderer: parses the resolved file and lays it out as a scrollable Table (row-and-column grid) rather than dumping raw delimited text, which is how AttachmentType.csv used to be rendered before it had its own renderer (it fell back to the plain-text renderer, which just showed the raw file content unparsed).
DefaultConnectivityChecker
DownloadClient
Minimal HTTP client abstraction the download manager depends on, so tests can inject a fake implementation instead of hitting the network. The default implementation wraps Dio.
DownloadConfig
Controls download timeouts, retry/backoff, concurrency and resume.
DownloadFailed
DownloadManager
Orchestrates downloads with progress reporting, cancellation and retry.
DownloadProgress
Progress update for an in-flight download.
DownloadResult
Result of a completed download attempt.
ExpiredUrl
ExternalOpenConfig
Controls whether unsupported/disabled attachments may fall back to an OS-provided external viewer.
ExternalOpenDisabled
Falling back to an external, OS-provided viewer was required (renderer disabled/unsupported, or Office-on-Android) but ExternalOpenConfig.allowExternalFallback is false, so no fallback was attempted.
FileAttachmentSource
FileBasedMetadataStore
Pure-Dart, file-based implementation of AttachmentMetadataStore: a single JSON index file ({fileName}.json) under the app-support directory maps keys to CacheEntry maps. There is no native dependency and no third-party package (replaces hive/hive_flutter).
FormatDetector
Detects an AttachmentType using, in priority order:
HtmlAttachmentRenderer
Renders local/remote/cached HTML (also used by SCORM/H5P entry points) via the official webview_flutter package (flutter.dev-published) — this is the one capability where a battle-tested official plugin covers Android/iOS/macOS uniformly, so there's no hand-written native webview channel in this engine at all.
ImageAttachmentRenderer
Full-view image renderer with pinch-to-zoom / pan via InteractiveViewer.
InFlightRegistry<T>
Deduplicates concurrent operations that share the same logical key (e.g. Attachment.stableIdentity) so that N simultaneous callers requesting the same attachment trigger exactly one underlying operation, with every caller awaiting the same shared Future.
InMemoryPdfPageMemory
Session-only PdfPageMemory: fast, dependency-free, and the default for PdfAttachmentRenderer. Cleared when the app process restarts.
InsufficientStorage
InvalidSource
MagicSignature
A single magic-byte signature: bytes to match at a given offset.
NativeAudioController
Replaces just_audio. iOS: AVFoundation (AVAudioPlayer for local files, AVPlayer for streaming remote URLs). Android: Media3/ExoPlayer (falls back to MediaMediaPlayer API-shape) for local/streaming audio.
NativeDownloadClient
Replaces dio: downloads over a hand-written native transport (URLSessionDownloadTask on iOS, HttpURLConnection on Android) owned by the platform implementation package, streaming progress/completion/ error events back through AttachmentEnginePlatform, while still satisfying the existing DownloadClient interface so DownloadManager's retry/queue logic is unchanged.
NativeOpenChannel
Replaces open_filex. iOS: UIDocumentInteractionController (falls back to QLPreviewController-style presentation). Android: Intent.ACTION_VIEW with a FileProvider content URI, inferred MIME type, and grantUriPermission.
NativeOpenResult
Result of an "open externally" request (hand the file to another app).
NativePathsChannel
Replaces path_provider: asks the native side (via AttachmentEnginePlatform) for app-private storage directories.
NativePdfController
Replaces pdfx. Talks to native PDF rendering through AttachmentEnginePlatform: iOS: PDFKit (PDFDocument, PDFPage.thumbnail). Android: android.graphics.pdf.PdfRenderer.
NativePlaybackStatus
NativeShareChannel
Replaces share_plus. iOS: UIActivityViewController. Android: Intent.ACTION_SEND with a FileProvider content URI.
NativeVideoController
Replaces video_player. Rendering happens via a native FlutterPlatformView, built by AttachmentEnginePlatform.videoBuildView: iOS embeds an AVPlayerViewController's view through a UiKitView (tradeoff: pulls in the system playback chrome unless customized further — acceptable for this pass, see README); Android embeds a Media3 PlayerView through an AndroidView.
NetworkUnavailable
NoopAttachmentDiagnosticsSink
No-op default sink used when a host app doesn't provide one.
OfficeAttachmentRenderer
Renders office documents (doc/docx/xls/xlsx/ppt/pptx/odt/...).
OfficeConversionStrategy
Extension point for a host app to plug in server-side or on-device office-to-PDF conversion (there is no server in this fresh project to call). If provided, OfficeAttachmentRenderer will use it to obtain a renderable PDF path instead of falling back to the OS document viewer.
OfflineDocxViewer
Fully offline DOCX renderer — no network access at any point.
OfflinePptxViewer
Fully offline PPTX renderer — no network access at any point.
OfflineSpreadsheetViewer
Fully offline spreadsheet renderer (.xlsx and legacy .xls) — no network access at any point.
PdfAttachmentRenderer
Full-view PDF renderer backed by NativePdfController, which wraps native PDFKit (iOS) / PdfRenderer (Android) rendering — see README for justification. Zoom/scroll stays purely in Dart via InteractiveViewer around the rendered page image; paging is handled with a PageView of per-page rendered bitmaps.
PdfPageMemory
Remembers the last-viewed page per attachment so reopening a PDF resumes where the reader left off, instead of always restarting at page one.
PermissionDenied
PlaybackFailed
PreviewConfig
Controls thumbnail/preview behavior surfaced to preview widgets.
RendererConfig
Enables/disables full-view rendering per AttachmentType.
RendererDisabledByConfig
The renderer for this attachment's type has been disabled via RendererConfig. Distinct from UnsupportedAttachment, which means the format simply has no renderer at all — this means a renderer exists but the host explicitly turned it off.
RendererFailed
RendererRegistry
Registry mapping AttachmentType to the AttachmentRenderer used to build its full-viewer widget, falling back to UnknownAttachmentRenderer (external-open affordance) for anything unregistered.
ResolvedAttachment
Result of AttachmentResolver.resolve: a local, usable file plus the (possibly updated) attachment metadata and its computed capabilities.
ScormAttachmentRenderer
Detects a SCORM package (a zip containing imsmanifest.xml), extracts it safely into an app-private directory (guarding against zip-slip path traversal via extractArchiveSafely), and launches its entry HTML in HtmlAttachmentRenderer. Falls back to UnknownAttachmentRenderer's external-open affordance if the package can't be resolved.
ServerAttachmentSource
StreamAttachmentSource
TextAttachmentRenderer
Plain text viewer. Set snippetMode to true (via TextAttachmentRenderer.preview) for a short, non-scrolling preview rather than the full document.
Unauthorized
UnknownAttachmentRenderer
Fallback renderer used for AttachmentType.unknown, any type without a registered renderer, or any type disabled via RendererConfig: shows a generic icon and offers external open / download instead of failing outright, unless externalOpenConfig disallows the external fallback, in which case it reports a disabled state instead of an open affordance.
UnknownFailure
UnsupportedAttachment
UrlAttachmentSource
VideoAttachmentRenderer
VideoControllerPool
Shared pool of NativeVideoControllers keyed by source, so navigating to the same video twice reuses (rather than duplicates) a controller, and controllers are always disposed exactly once.
ZipEntry
A single entry (file or directory) parsed from a ZIP central directory.
ZipReader
Minimal hand-written ZIP reader that parses the End-Of-Central-Directory record and Central Directory File Headers to list entries, and reads individual entries' bytes via their Local File Header, using ZLibDecoder (raw-deflate mode) from dart:io (SDK-provided) for DEFLATE decompression. Supports STORED (0) and DEFLATE (8) methods only, which covers the overwhelming majority of zip/SCORM/office packages in practice.

Enums

AttachmentStatus
Lifecycle status of an Attachment as it moves through discovery, resolution, caching, rendering and cleanup.
AttachmentType
The high-level kind of content an Attachment represents.
CacheEntryCategory
Category of a cached file, used to allow selective eviction (e.g. clear all thumbnails without touching full originals).
DownloadRetryBackoff
Backoff strategy applied between retried download attempts.
DownloadState
States a queued download can be in.
NativePlaybackState
Playback/buffering state mirrored from the native side.
PreviewPreloadPolicy
How aggressively adjacent/related previews are warmed ahead of time.

Constants

kExtensionToMimeType → const Map<String, String>
Hand-written extension -> MIME type lookup table, replacing the third-party mime package. Covers the formats referenced by FormatDetector / the capability spec. Not exhaustive, but sufficient for attachment-type classification purposes.

Properties

defaultPdfPageMemory InMemoryPdfPageMemory
A single, shared InMemoryPdfPageMemory so page position survives across renderer instances (e.g. leaving and reopening the same attachment) within one app session, without callers having to thread one through themselves.
final
kMagicSignatures List<MagicSignature>
Known magic-byte signatures for common attachment formats. Checked in order; more specific signatures should be listed before generic ones (e.g. zip-based office/scorm formats are disambiguated by extension after the generic zip signature matches).
final

Functions

archiveContainsScormManifest(File archiveFile) Future<bool>
Returns true if archiveFile contains an imsmanifest.xml at (or near) its root, indicating a SCORM package.
archiveReaderContainsScormManifest(ZipReader reader) bool
Returns true if reader's archive contains an imsmanifest.xml at (or near) its root, indicating a SCORM package. Use this (rather than archiveContainsScormManifest) when you already have a decoded ZipReader for the file — e.g. because you're about to pass it to extractArchiveSafely too — to avoid reading and decoding the whole archive a second time.
detectTypeFromMagicBytes(Uint8List bytes) AttachmentType?
Detects a type from raw bytes using kMagicSignatures. Returns null if no signature matches. Note WEBP and WAV both use a RIFF container; the caller should disambiguate further using bytes 8-11 if needed.
extractArchiveSafely(File archiveFile, Directory targetDir, {ZipReader? reader}) Future<List<String>>
Safely extracts a zip archiveFile into targetDir, rejecting any entry that could escape the target directory via .., an absolute path, or a symlink. Returns the list of extracted file paths.
lookupMimeTypeForExtension(String extensionOrFileName) String?
Looks up the MIME type for a file name/extension. Mirrors mime's lookupMimeType('file.$ext') for the subset of formats this plugin cares about.
sanitizeForLog(String url) String
Strips the query string from url before logging, so signed-URL parameters and Authorization-style tokens embedded in query params are never written to logs. Only ever call this on values that might reach a log statement - the engine's core logic should prefer ids over URLs.

Typedefs

AttachmentRendererBuilder = Widget Function(BuildContext context, Attachment attachment)
Builds the full-viewer widget for a resolved, ready-to-render Attachment. localPath is guaranteed non-null/existing by the time a renderer is invoked (resolution happens upstream).
AttachmentResolveCallback = Future<ResolvedAttachment> Function(Attachment attachment)
Signature for a caller-supplied resolve step. Defaults to AttachmentManager.instance.open.
HiveCacheMetadataStore = FileBasedMetadataStore
Backwards-compatible alias used before the Hive removal.

Exceptions / Errors

AttachmentConfigValidationError
Thrown when an AttachmentEngineConfig (or one of its sub-configs) is constructed with an invalid combination of values.
AttachmentResolutionException
Result type used internally to thread failures without throwing across resolution stages.
DownloadCancelledException
Thrown when DownloadManager.cancel is called for a download that was still queued (waiting for a concurrency slot) rather than actually in flight — cancelling a queued download has no DownloadClient cancel token to forward to, so it's handled entirely inside DownloadManager by never letting the queued request acquire a slot.