xnz_image 0.2.5
xnz_image: ^0.2.5 copied to clipboard
Flutter image component for unified loading, downloading, and caching of network/memory/file/asset images, with extensible SVG/AVIF format support.
XNZImage #
English | δΈζ
A Flutter image loading package with unified rendering and cache management. It supports network, memory, file, and asset image sources, and provides an extensible architecture for extra formats.
Overview #
This repository uses a core + extension architecture:
xnz_image: main public API (XNZNetworkImage,XNZMemoryImage,XNZFileImage,XNZAssetImage,XNZCacheManager, etc.)xnz_image_core: internal capabilities (cache, downloader, extension mechanism)xnz_image_svg: SVG support extensionxnz_image_avif: AVIF support extension
Component Reference #
For a code-level component behavior analysis (Chinese), see:
Installation #
1) Base package #
dependencies:
xnz_image: ^0.2.5
2) Optional extensions #
dependencies:
xnz_image: ^0.2.5
xnz_image_svg: ^0.2.5
xnz_image_avif: ^0.2.5
For local development in this monorepo, you may use path dependencies.
Register Extensions #
import 'package:flutter/widgets.dart';
import 'package:xnz_image/xnz_image.dart';
import 'package:xnz_image_svg/xnz_image_svg.dart';
import 'package:xnz_image_avif/xnz_image_avif.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
XNZImageLogs.showLogs = true;
XNZImageMemoryObserver().init();
XNZImage.support(XNZImageSvg());
XNZImage.support(XNZImageAvif());
runApp(const MyApp());
}
Logging #
Runtime logs are now normalized to:
[module][action][module][action][key=value,...]
Example:
[XNZImageDownloader][task_start][requestKey=...,url=https://...]
XNZImageLogs.event(module, action, fields: {...}) is recommended for internal/extension code.
XNZImageLogs.setInterceptor(...) supports both interceptor signatures:
void Function(String tag, String message)(legacy compatible)bool Function(String tag, String message)(truemeans default output is intercepted)
XNZImageLogs.logFilter supports quick category filtering:
XNZImageLogFilter.all(default)XNZImageLogFilter.successXNZImageLogFilter.failure
XNZImageLogs.showLogs = true;
XNZImageLogs.logFilter = XNZImageLogFilter.failure; // only failure logs
Observable Events By Source #
- Network (
XNZNetworkImage/XNZNetworkImageProvider)- load status:
load_status_downloading,load_status_complete,load_status_failed - cache path:
get_memory_cache_hit|miss,get_disk_cache_hit|miss - download path:
task_start,task_complete,task_failed,task_reuse_shared - decode path:
decode_probe,decode_success,decode_failed,decode_failed_final
- load status:
- Memory (
XNZMemoryImage/XNZMemoryImageProvider)- widget flow:
build_start,resolve_complete,display_failed - decode path:
decode_probe,decode_success,decode_failed,load_failed
- widget flow:
- File (
XNZFileImage/XNZFileImageProvider)- widget flow:
build_start,resolve_complete,display_failed - decode path:
decode_probe,decode_success,decode_failed,load_failed
- widget flow:
- Asset (
XNZAssetImage/XNZAssetImageProvider)- widget flow:
build_start,resolve_complete,display_failed - decode path:
decode_probe,decode_success,decode_failed,load_failed
- widget flow:
Notes:
download_*and network cache hit/miss events are network-only by design.decode_probeincludesformatandlikelyImage, useful for quick format mismatch diagnosis.- delegated/custom provider display failures emit
XNZProxyImageStreamCompleter.display_failed.
Supported Formats #
- Default (
xnz_imageonly): common bitmap formats such aspng,jpg,jpeg,gif,webp,bmp - With
xnz_image_svgregistered:svg,svgz - With
xnz_image_avifregistered:avif,avifs(including animated AVIF duration override)
Basic Widgets #
Network #
XNZNetworkImage(
imageUrl: 'https://picsum.photos/800/480',
width: 300,
height: 180,
fit: BoxFit.cover,
)
Memory #
XNZMemoryImage(
bytes: yourBytes,
width: 300,
height: 180,
fit: BoxFit.cover,
)
File #
XNZFileImage(
file: File('/path/to/local/image.avif'),
width: 300,
height: 180,
fit: BoxFit.cover,
)
Asset #
XNZAssetImage(
assetName: 'assets/images/banner.avif',
width: 300,
height: 180,
fit: BoxFit.cover,
)
Use With Flutter Image #
Image(
image: XNZNetworkImageProvider('https://picsum.photos/800/480'),
width: 300,
height: 180,
fit: BoxFit.cover,
)
Authenticated Network Requests #
XNZNetworkImage and XNZNetworkImageProvider support optional headers.
XNZNetworkImage(
imageUrl: 'https://example.com/private/image.png',
headers: {'Authorization': 'Bearer <token>'},
cacheKeyStrategy: XNZCacheKeyStrategy.urlAndHeaders,
)
Cache Key Strategy #
cacheKeyStrategy controls cache isolation behavior:
XNZCacheKeyStrategy.urlOnly(default): URL-only cache key, better hit-rateXNZCacheKeyStrategy.urlAndHeaders: URL+headers cache key, safer isolation
Note: request de-duplication always uses URL+headers internally to avoid mixing in-flight downloads from different header contexts.
Animated Image Playback #
XNZAnimatedImage decodes frames and plays on a timeline. It is suitable for GIF / Animated WebP / APNG,
and can also decode animated AVIF when AVIF support is registered.
Highlights #
- Timeline sync:
position,progress,frameIndex - Playback control:
play,pause,resume,replay - Callbacks:
onLoaded,onCompleted - Loop control:
loop - Error fallback:
errorBuilder
Maintenance Updates (2026-05-12) #
- Cache-key hashing now uses SHA-256 full-length lowercase hex on all runtimes.
- Animated network bytes loading on IO now goes through the same downloader/cache pipeline as normal network images (
XNZUrlRequest+XNZImageDownloader+XNZCacheManager), socacheKeyStrategybehavior is consistent. - AVIF codec keys now use process-local monotonically increasing integers instead of object-hash-derived values, reducing native decoder key-collision risk.
- AVIF async init/decode guardrails were improved (explicit init error propagation and safer completer handling).
- Added regression tests for:
- unified IO animated cache-path loading;
- AVIF codec key monotonicity.
Maintenance Updates (2026-05-11) #
- Optimized animated memory cache-key generation: memory bytes fingerprints are now cached by bytes identity to avoid repeated full-byte hashing on frequent rebuilds.
- Refactored
XNZAnimatedImageinternals intolib/src/animated/modules (loader/decoder/cache-key/provider-context/models/controller/cache) to reduce single-file complexity. - Unified resolve/render flow for
XNZAssetImage,XNZMemoryImage,XNZFileImage, andXNZNetworkImagevia shared helpers, reducing template duplication and future drift risk. - Public API remains backward compatible.
Example #
final controller = XNZAnimatedImageController();
XNZAnimatedImage(
image: XNZNetworkImageProvider('https://example.com/demo.gif'),
controller: controller,
autoPlay: true,
loop: true,
fit: BoxFit.contain,
onLoaded: (duration, fps, frameCount) {
debugPrint('duration=$duration fps=$fps frames=$frameCount');
},
onCompleted: (completedLoops) {
debugPrint('completedLoops=$completedLoops');
},
loadingBuilder: (_) => const Center(child: CircularProgressIndicator()),
errorBuilder: (context, error, stackTrace) {
return const Center(child: Text('Animated image load failed'));
},
)
Unified Render Hook (renderBuilder) #
Use renderBuilder to wrap default rendering behavior for bitmap and custom formats:
XNZNetworkImage(
imageUrl: url,
renderBuilder: (context, child) {
return ClipRRect(
borderRadius: BorderRadius.circular(12),
child: child,
);
},
)
If renderBuilder returns null, the widget falls back to default rendering.
Cache Manager #
import 'dart:typed_data';
import 'package:xnz_image/xnz_image.dart';
Future<void> printCacheUsage() async {
final cacheManager = XNZCacheManager();
final memoryBytes = cacheManager.getMemoryCacheBytes();
final memoryMaxBytes = cacheManager.getMemoryCacheMaxBytes();
final diskBytes = await cacheManager.getDiskCacheBytes();
print('Memory cache: $memoryBytes B');
print('Memory cache max: $memoryMaxBytes B');
print('Disk cache: $diskBytes B');
}
Future<void> clearAllCache() async {
await XNZCacheManager().clearAll();
}
Future<void> clearUnusedDiskCache() async {
// Delete disk cache entries not used for 30 days.
final deleted = await XNZCacheManager().clearUnusedDiskCache(
const Duration(days: 30),
);
print('Deleted disk cache files: $deleted');
}
Future<void> configureDiskCacheTtl() async {
final cacheManager = XNZCacheManager();
// Global default TTL for disk cache writes.
cacheManager.setDiskCacheDefaultTtl(const Duration(hours: 24));
// Per-write override.
await cacheManager.setCache(
XNZUrlRequest('https://example.com/avatar.png'),
Uint8List(0),
ttlOverride: const Duration(hours: 1),
);
}
Platform Notes #
- Web supports
XNZNetworkImage,XNZMemoryImage,XNZAssetImage, andXNZAnimatedImage. - Web enables memory cache only (disk cache is disabled).
- Web memory cache defaults to 48MB (native platforms default to 300MB).
XNZFileImage/XNZFileImageProviderare not supported on Web and throwUnsupportedError.
Run Example #
Minimal pub example:
flutter run -t example/main.dart
More complete demo apps are under:
examples/example_bitmapexamples/example_bitmap_svgexamples/example_bitmap_avifexamples/example_bitmap_svg_avif