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
CapabilityEnginefrom 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.allowExternalFallbackis 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 (replaceshive/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_flutterpackage (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 (AVAudioPlayerfor local files,AVPlayerfor streaming remote URLs). Android: Media3/ExoPlayer (falls back toMediaMediaPlayerAPI-shape) for local/streaming audio. - NativeDownloadClient
-
Replaces
dio: downloads over a hand-written native transport (URLSessionDownloadTaskon iOS,HttpURLConnectionon Android) owned by the platform implementation package, streaming progress/completion/ error events back throughAttachmentEnginePlatform, while still satisfying the existing DownloadClient interface so DownloadManager's retry/queue logic is unchanged. - NativeOpenChannel
-
Replaces
open_filex. iOS:UIDocumentInteractionController(falls back toQLPreviewController-style presentation). Android:Intent.ACTION_VIEWwith aFileProvidercontent URI, inferred MIME type, andgrantUriPermission. - NativeOpenResult
- Result of an "open externally" request (hand the file to another app).
- NativePathsChannel
-
Replaces
path_provider: asks the native side (viaAttachmentEnginePlatform) for app-private storage directories. - NativePdfController
-
Replaces
pdfx. Talks to native PDF rendering throughAttachmentEnginePlatform: iOS: PDFKit (PDFDocument,PDFPage.thumbnail). Android:android.graphics.pdf.PdfRenderer. - NativePlaybackStatus
-
Replaces
share_plus. iOS:UIActivityViewController. Android:Intent.ACTION_SENDwith aFileProvidercontent URI. - NativeVideoController
-
Replaces
video_player. Rendering happens via a nativeFlutterPlatformView, built byAttachmentEnginePlatform.videoBuildView: iOS embeds anAVPlayerViewController's view through aUiKitView(tradeoff: pulls in the system playback chrome unless customized further — acceptable for this pass, see README); Android embeds a Media3PlayerViewthrough anAndroidView. - 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 (
.xlsxand 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. - 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
mimepackage. 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
archiveFilecontains animsmanifest.xmlat (or near) its root, indicating a SCORM package. -
archiveReaderContainsScormManifest(
ZipReader reader) → bool -
Returns true if
reader's archive contains animsmanifest.xmlat (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
archiveFileintotargetDir, 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'slookupMimeType('file.$ext')for the subset of formats this plugin cares about. -
sanitizeForLog(
String url) → String -
Strips the query string from
urlbefore 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.
localPathis 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.