piksel 0.2.0 copy "piksel: ^0.2.0" to clipboard
piksel: ^0.2.0 copied to clipboard

A type-safe, extensible image loading and caching pipeline for Flutter — network, SVG, AVIF, GIF, video-frame and blurhash behind one API.

piksel #

A type-safe, extensible image loading & caching pipeline for Flutter. Network, SVG, AVIF, GIF, video frames and blurhash — one request model, one widget, one cache. The parts Flutter's engine already gives you (off-thread decode, ImageProvider) are kept; the parts it lacks (disk cache, format unification, transforms, retry, animation control) are built.

PikselImage.network('https://example.com/photo.jpg')

Why piksel #

  • Type-safe by construction. LoadState and ImageResult are sealed — the compiler makes you handle loading, success and error. No stringly-typed options, no silent fallthrough.
  • Fast where it counts. A warm memory hit renders in the widget's first build — no placeholder flash, no async hop, no extra rebuild. Cold loads run through bounded fetch/decode pools with in-flight dedup (20 identical grid cells = 1 fetch + 1 decode). Measured head-to-head in BENCHMARK.md.
  • Three cache tiers. Decoded images in memory (LRU, clone-on-read), decoded+transformed results on disk, raw downloads on disk. On web, disk tiers persist in IndexedDB.
  • Every format through one pipeline. GIF and animated WebP play out of the box; SVG, AVIF and video frames are one-line plugin registrations.
  • Resilient. Exponential-backoff retry for transient failures (connection errors, HTTP 408/429/5xx); permanent failures fail fast. Cooperative cancellation stops off-screen downloads mid-stream.
  • Scroll-aware prefetch. PikselPrefetcher warms the next tiles in the direction of travel from your itemBuilder — bounded so it never starves visible loads, cancelled the moment the scroll direction reverses.

Quick start #

// 1. Common case — one line.
PikselImage.network('https://…/photo.jpg', width: 120, height: 120)

// 2. Full control: transforms, placeholder, typed exhaustive state.
PikselImage(
  ImageRequest(
    source: NetworkSource(url, headers: auth),
    transformations: const [RoundedCorners(16), Blur(4)],
    placeholder: BlurhashStateImage(hash),
    error: IconStateImage(Icons.broken_image),
  ),
  builder: (context, state) => switch (state) {
    LoadLoading(:final progress) => LinearProgressIndicator(value: progress),
    LoadSuccess(:final result)   => RawImage(image: result.image),
    LoadError()                  => const Icon(Icons.error),
  },
)

// 3. Animated sources play automatically; pin the first frame if you want.
PikselImage.network('https://…/animation.gif', animate: false)

// 4. Drops into ANY Flutter widget via ImageProvider interop.
final provider = request.toImageProvider();
Image(image: provider);
BoxDecoration(image: DecorationImage(image: provider));

// 5. Imperative — prefetch / non-widget.
await Piksel.instance.prefetch(request);
final result = await Piksel.instance.execute(request);

Configure once (no forced singleton — hold your own instance for DI):

Piksel.configure(Piksel(
  memoryCache: MemoryCache.lru(maxSizeBytes: 128 << 20),
  retryPolicy: const RetryPolicy(maxAttempts: 3),
  components: const ComponentRegistry(
    decoders: [SvgDecoder(), AvifDecoder()], // from plugin packages
  ),
));

Architecture #

Widget / DX      PikselImage · request.toImageProvider()
Engine           Piksel · RequestInterceptor chain (retry) · dedup · pools
Components       Fetcher · Decoder (+AnimatedDecoder) · Transformation
Cache            MemoryCache (decoded) · ResultCache (disk) · DownloadCache (disk)
Data             ImageSource · DataSource · Fetch/DecodeResult
Platform         dart:io files (native/desktop)  ·  IndexedDB (web)

Type-safety #

  • sealed LoadState / sealed ImageResult — the compiler forces you to handle loading, success and error.
  • ImageSource variants (Network/Asset/File/Bytes) — fetchers dispatch on the type; plugins add their own sources (VideoFrameSource).
  • enum CachePolicy with readAllowed/writeAllowed; Precision, Scale.
  • Extra<T> — typed request metadata instead of Map<String, Object?>.

Concurrency (correctness under image-heavy grids) #

  • Bounded parallelism — separate Semaphore pools for fetch vs decode, so a 200-cell grid can't open 200 sockets or decode 200 bitmaps at once.
  • In-flight dedup — N widgets requesting the same URL share one fetch+decode.
  • ui.Image ref-safety — the memory cache owns a private clone and hands every caller its own clone; a consumer disposing its handle can never free pixels still on screen.
  • Cooperative cancellationCancelToken checked at every stage boundary; widget disposal cancels in-flight work.
  • Size-aware decode — images are down-sampled to the box they'll paint into (kills OOM-on-grids without manual cacheWidth).

Extensibility #

Three seams, each one interface:

class SvgDecoder implements Decoder {
  bool handles(FetchResult f) => f.mimeType == 'image/svg+xml';
  Future<DecodeResult> decode(ctx, f) async { /* … */ }
}
  • Fetcher — teach piksel a new source.
  • Decoder — teach it a new format; sniff bytes via DataSource.magic. Implement AnimatedDecoder to provide multi-frame playback.
  • Transformation — pixel operations that participate in cache keys.

Interceptors (RequestInterceptor / DecodeInterceptor) wrap the chain for cross-cutting concerns — retry and the result cache are themselves interceptors.

Format plugins #

Package Adds
piksel_svg SVG rendering through the same widget + cache
piksel_avif AVIF stills and animation
piksel_video_frame Frame extraction from video URLs/files
piksel_blurhash Decoded blurhash placeholders

Each is a separate package; the core depends on none of them.

Performance #

Measured, not claimed: BENCHMARK.md has a reproducible head-to-head against the ecosystem — load latency, scroll frame timings, methodology, and honest caveats (including what's inside measurement noise).

0
likes
160
points
111
downloads

Documentation

API reference

Publisher

verified publisherandroidpoet.dev

Weekly Downloads

A type-safe, extensible image loading and caching pipeline for Flutter — network, SVG, AVIF, GIF, video-frame and blurhash behind one API.

Repository (GitHub)
View/report issues

Topics

#image #cache #image-loading #svg #avif

License

MIT (license)

Dependencies

collection, crypto, flutter, http, meta, path, path_provider, web

More

Packages that depend on piksel