attachment_engine 0.0.1-dev.4
attachment_engine: ^0.0.1-dev.4 copied to clipboard
Universal attachment engine for Flutter to resolve, cache, download, preview, and render documents, media, and web content using native platform APIs.
0.0.1-dev.4 #
TextAttachmentRenderer.showSearchnow defaults tofalse(wastrue). The in-file search bar remains available by opting in explicitly; the plain scrollable text view is now the out-of-the-box behavior.
0.0.1-dev.3 #
- New
OfficeAttachmentRenderer.onDismissedcallback, invoked once the user dismisses the iOS QuickLook office-document preview. Requiresattachment_engine_ios0.0.1-dev.2+, whoseopenOfficePreviewnow resolves only on the modal's actual dismissal rather than as soon as it is presented — previously there was no signal at all for a host app to react to the close (e.g. to pop the screen left behind it).
0.0.1-dev.2 #
- New "keep available offline" pinning:
AttachmentManager.pinForOffline(attachment)caches (if needed) and exempts an attachment from every automatic cache-cleanup path — size-cap LRU eviction,AttachmentCacheManager.clearUnused, and.clearExpired— so a file the user explicitly marked "save for offline"/"keep downloaded" survives storage pressure and won't quietly disappear.unpinFromOffline/isPinnedForOfflinereverse/query it. Pinning only exempts a file from automatic cleanup, not from an explicit, targeted removal:AttachmentManager.deleteCache(andAttachmentCacheManager.clearAttachment/.clearAll) still remove a pinned file when the host app genuinely needs to. A pinned entry also stays servable fromlookup()(cache hit) even past its remoteexpiresAt/retention window, since a signed URL's expiry describes the remote source, not whether the already-downloaded bytes are usable offline. NewCacheEntry.pinnedfield (defaults tofalse; absent in metadata written before this release, read back asfalse). - New
AttachmentManager.prefetch(url, {id, name})— warms the cache for a URL in the background without a caller having to build a fullAttachmentor open/render anything. Returns theResolvedAttachmenton success (nullon failure) — check.fromCacheto know whether this call actually downloaded anything or the content was already cached, and use.attachment/.localPathto show, share, or open the now-cached file anywhere else in the app without a second download. Best-effort: a failed prefetch doesn't throw, but is still reported through the configuredAttachmentDiagnosticsSink. Reuses the existing resolve pipeline (cache check → dedup via in-flight registry → download → cache write), so it gets every fix above for free. Caller must pass a stableidwhenurlis a short-lived signed URL — otherwise each rotated URL is treated as new content and never dedupes against a prior prefetch/open. - Perf: downloads, previews and offline Office viewers now read files
in 64 KB chunks (
RandomAccessFile) instead of one large synchronousreadAsBytesSync()/readAsBytes()call, keeping peak memory bounded and the event loop unblocked for large attachments. Chunking was applied only where it genuinely helps (file reads); it was deliberately not applied to CSV/ZIP parsing or small metadata files, where it would add complexity without benefit (already-loaded in-memory data, or a background isolate is used instead — see the CSV/ZIP entries below). - Perf:
FileBasedMetadataStorenow debounces (2s, configurable) cache-metadata disk writes instead of rewriting the whole index file on every singleput()/delete()— bursts of cache activity (e.g. resolving a grid of attachments) now cost one disk write, not N.AttachmentCacheManager/AttachmentManagerforce-flush any pending write before it could be lost: immediately after a genuinely new cache entry is written (offline durability — a force-quit right after caching something still finds it on next launch, even from cache with no network), and ondispose(). - Fix: closed a concurrency race where resolving several different
attachments at once (e.g. a grid) could interleave their
read-decide-mutate cache-eviction sequences and let the on-disk cache
silently exceed its configured
maxTotalSizeBytes. AllAttachmentCacheManagermutations (write,clearExpired,clearAttachment,clearUnused,clearAll) are now serialized against each other. - Fix: a deliberately cancelled download (
DownloadManager.cancel) was being treated as a transient failure and silently retried instead of staying cancelled. A still-queued (not yet running) download can now also be cancelled before it ever starts. - Fix: added
dispose()across the engine's layers (AttachmentCacheManager,DownloadManager,NativeDownloadClient,AttachmentResolver,AttachmentManager) to releaseTimers, stream controllers, and the native download platform-channel subscription.AttachmentManager.initializeDefault()now disposes any existing singleton before replacing it, instead of leaking it. - Breaking: removed
DownloadQueue/QueuedDownload/DownloadPriority(src/download/download_queue.dart) — dead code with no effect on actual download scheduling (never constructed or referenced byDownloadManageror anything else in the package). - Fix:
AttachmentResolvernow actually populatesattachmentTypeviaFormatDetector(from extension/mime/URL, plus magic bytes when freshly downloaded) when a resolved attachment's type is stillunknown— previously it never did this, so anyAttachmentbuilt without an explicitattachmentType(the documented minimal id/name/source usage) stayedunknownforever and always fell through toUnknownAttachmentRenderer, regardless of its extension. AttachmentViewernow auto-resolves an unresolvedAttachmentinternally (viaAttachmentManager.instance.openby default, overridable), showing loading/error states — callers no longer need to pre-resolve before constructing it.AttachmentManager.initializeDefault()accepts an optionaldownloadClient/connectivityCheckerso a host app can route downloads through its own auth-aware HTTP client.- New
AttachmentType.csvwithCsvAttachmentRenderer(RFC 4180-style parsing, rendered as a table) —.csv/.tsv(auto-detected delimiter) are no longer misclassified as plain text. PdfAttachmentRendererremembers the last-viewed page per attachment (PdfPageMemory, session-scoped by default, overridable/persistable) and shows a retry affordance on open/page-render failure.OfficeAttachmentRenderernow prefers in-app viewing at every step, external-open only as the genuine last resort: iOS QuickLook → bundled genuinely-offline in-app renderers on Android (.docxviaOfflineDocxViewer,.xlsx/.xls/.xlsmviaOfflineSpreadsheetViewer,.pptxviaOfflinePptxViewer— all zero-network, bundled open-source JS libraries, seeassets/office_offline/README.md) → Microsoft Office Online (needs a connection) → optionalconversionStrategy(deprioritized, lossy) → external-open. Legacy.doc/.pptand OpenDocument formats remain uncovered by an offline renderer — documented, deliberate gap (no suitable renderer exists for the former; the only option for the latter, WebODF, is AGPL-licensed).TextAttachmentRenderer's full (non-snippet) view now has an in-file search bar (case-insensitive, match counter, next/previous navigation) — setshowSearch: falseto opt out..xlsmis now classified asAttachmentType.office(was falling through to a generic mime lookup).- Minimum Dart SDK raised to
^3.13.0(dot-shorthand syntax used throughout the renderers).
0.0.1-dev.1 #
Initial real release, after a full native rewrite and hardening pass.
- Fully-native platform channels for PDF, video, audio, webview, share,
open-externally, download, and paths — no third-party plugin
dependencies. Minimal pub deps:
plugin_platform_interface,crypto,meta,equatable. - Pure-Dart JSON-file metadata store and hand-written ZIP reader (no
Hive/
archivedependency). - Attachment resolve → cache (LRU, checksum-verified) → download → render
pipeline with in-flight request de-duplication and typed
AttachmentFailures. - HTTP range-based download resume on both platforms: Android
(
HttpURLConnection+Rangeheader +.part.metasidecar), iOS (URLSessionDownloadTaskresume-data). Falls back to a clean full restart when the server/OS doesn't support resuming. - HLS (
.m3u8) adaptive playback works on both platforms via native APIs (AVPlayeron iOS,MediaPlayeron Android). DASH (.mpd) is not supported — documented limitation of avoiding ExoPlayer/Media3. - Security: zip-slip-safe archive extraction, hashed (never raw) cache filenames, no URLs/tokens ever logged, stable cache identity independent of rotating signed URLs.
- Widgets:
AttachmentTile,AttachmentErrorView,AttachmentList,AttachmentViewer. - CI: GitHub Actions running format/analyze/test/Android build on
ubuntu-latestand an iOS build job onmacos-latest.