crash_safe_image 0.2.1
crash_safe_image: ^0.2.1 copied to clipboard
Crash-safe image widget for Flutter. Auto-detects network/asset/file/memory sources, shows friendly placeholders & error UI, and uses cached_network_image for smart caching. Includes ImageProvider.
crash_safe_image #
Crash‑safe image widget for Flutter. It auto‑detects image source (network / asset / file / memory), shows friendly placeholders & error UI, and uses cached_network_image
for smart caching.
Minimal API · Safe defaults · Drop‑in replacement for many
Image.*
cases
Table of contents #
- Features
- Why CrashSafeImage?
- Install
- Quick start
- How it decides the source
- API
- Examples
- Platform support
- Performance notes
- Version compatibility
- Roadmap
- Contributing
- License
Features #
- ✅ Auto‑detects:
http/https
→ network ·file://
/absolute path → file · no scheme → asset ·bytes
→ memory - ✅ SVG support: auto-detects
.svg
for network/asset/file/memory, powered byflutter_svg
- ✅ Crash‑safe defaults: shows placeholder while loading and a clean error UI on failure
- ✅ Network caching via
cached_network_image
- ✅ Works in lists/grids with optional fade‑in/out
- ✅ ImageProvider getter for
CircleAvatar
, `DecorationImage (never-null with transparent fallback) - ✅ Customizable: size, fit, alignment, borderRadius, color, opacity, blend mode, HTTP headers & cacheKey
---Performance notes
Why Crash Safe Image #
- Stops avoidable crashes: invalid asset keys, missing files, empty/null sources → you get a friendly error UI instead of exceptions.
- One widget, four sources: network • asset • file • memory — no
if/else
soup. - Better UX by default: placeholders and fade‑in/out already wired.
- Caches network images out of the box via
cached_network_image
. - Works with existing APIs: grab
.provider
forCircleAvatar
/DecorationImage
. - Easy to style:
fit
,alignment
,borderRadius
,opacity
,colorBlendMode
. - Zero null checks for Avatar/Decoration:
provider
is never null—bad/empty/SVG sources fall back to a transparent pixel. - Lean & tested: small surface area with widget tests.
When should I use it?
- Dynamic sources from API/user input where failures must not crash UI.
- Avatars, logos, list/grid images with consistent placeholders.
When might I not need it?
- You need custom decoding/stream control or web‑only file paths (until web‑safe mode ships).
Before vs After
// Before: branching + manual fallbacks
Widget buildAvatar(String? url) {
if (url == null || url.isEmpty) return const Icon(Icons.person);
return Image.network(url, errorBuilder: (_, __, ___) => const Icon(Icons.person));
}
// After: one line
CrashSafeImage(url, width: 40, height: 40, errorBuilder: (_) => const Icon(Icons.person));
Install #
Add to your pubspec.yaml
:
dependencies:
crash_safe_image: ^0.1.0
Import:
import 'package:crash_safe_image/crash_safe_image.dart';
Quick start #
CrashSafeImage(
'https://example.com/pic.png',
width: 80,
height: 80,
placeholderBuilder: (_) => const Center(child: Text('Loading...')),
errorBuilder: (_) => const Icon(Icons.error_outline),
);
Using memory bytes (e.g., from camera/DB):
CrashSafeImage(
null,
bytes: myUint8List,
width: 120,
height: 120,
fit: BoxFit.cover,
);
Using as provider (e.g., for CircleAvatar
):
CircleAvatar(
radius: 24,
backgroundImage: CrashSafeImage('https://example.com/avatar.png').provider, // never null
//No null checks needed—provider is always safe.
);
SVG examples
// Network SVG
CrashSafeImage.network('https://example.com/icon.svg', width: 40, height: 40);
// Asset SVG
CrashSafeImage.asset('assets/icons/logo.svg', width: 80);
// File SVG
CrashSafeImage.file(File('/storage/emulated/0/Download/icon.svg'));
// Raw SVG string
CrashSafeImage.svgString('<svg viewBox="0 0 24 24">...</svg>', width: 48);
How it decides the source #
- If
bytes != null && bytes.isNotEmpty
→ MemoryImage - Else if
name == null || name.trim().isEmpty
→ error UI - Else if
name
ishttp
/https
→ CachedNetworkImage /CachedNetworkImageProvider
- Else if
name
looks like a file path (e.g.,file://
, absolute) → FileImage - Else → AssetImage
Note: If the asset path is wrong or the file doesn’t exist, the widget shows the error UI instead of crashing.
API #
Constructor parameters #
Param | Type | Default | Notes |
---|---|---|---|
name |
String? |
– | Path/URL/asset key. Can be null when using bytes . |
width , height |
double? |
– | Final rendered size. |
fit |
BoxFit? |
– | How to inscribe the image into the box. |
alignment |
AlignmentGeometry |
Alignment.center |
Alignment for the image. |
borderRadius |
BorderRadius? |
– | Rounded corners; applied via ClipRRect . |
clipBehavior |
Clip |
Clip.hardEdge |
Clip strategy for rounding. |
color |
Color? |
– | Color filter tint. |
opacity |
Animation<double>? |
– | Animated opacity for Image.* . |
colorBlendMode |
BlendMode? |
– | Blend mode with color . |
placeholderBuilder |
WidgetBuilder? |
built‑in spinner | Builder for loading state. |
errorBuilder |
WidgetBuilder? |
built‑in broken‑image icon | Builder for error state. |
httpHeaders |
Map<String, String>? |
– | Extra headers for network requests. |
cacheKey |
String? |
– | Custom cache key for network images. |
fadeInDuration |
Duration |
250ms |
Network fade‑in. |
fadeOutDuration |
Duration |
250ms |
Network fade‑out. |
bundle |
AssetBundle? |
– | Custom bundle for assets. |
package |
String? |
– | Asset package name. |
bytes |
Uint8List? |
– | Raw bytes (memory images). |
Using as ImageProvider
#
Get a provider for places that need it (e.g., CircleAvatar
, BoxDecoration
):
final provider = CrashSafeImage('assets/logo.png').provider; // ImageProvider<Object>
The provider is never null. If the source is null/invalid/SVG, a 1×1 transparent
- MemoryImage is returned to keep Avatar/DecorationImage assertion-safe.
Custom placeholders & errors #
CrashSafeImage(
'https://invalid-url.example/broken.png',
width: 96,
height: 96,
placeholderBuilder: (_) => const Center(child: Text('Loading…')),
errorBuilder: (_) => const Icon(Icons.warning_amber_rounded, color: Colors.red),
);
Examples #
Basic
CrashSafeImage('assets/images/logo.png', width: 40, height: 40);
File path
CrashSafeImage('/storage/emulated/0/Download/picture.jpg', width: 120, height: 90);
DecorationImage
Container(
width: 160,
height: 100,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
image: DecorationImage(
image: CrashSafeImage('assets/bg.jpg').provider,
fit: BoxFit.cover,
),
),
);
Platform support #
- Android/iOS, macOS, Windows ✅
- Web: not officially supported yet if your app path relies on
dart:io
file checks. A web‑safe mode is on the roadmap (conditional imports).
Network and asset images work on the web, but direct local file paths (
FileImage
) do not.
Performance notes #
- The widget wraps content in a
SizedBox
+ClipRRect
for reliable sizing and rounded corners. - Avoid extremely frequent rebuilds with changing
cacheKey
or headers. - For very large images in lists, set an explicit
width/height
andfit
to reduce layout thrash. - Transparent fallback is a 1×1 PNG kept in memory; overhead is negligible.
Version compatibility #
- Dart SDK:
>=3.9.0 <4.0.0
- Flutter:
>=3.22.0
cached_network_image: ^3.4.1
See pubspec.yaml
for full constraints.
Roadmap #
- Web‑safe file detection (conditional
dart:io
imports) - More knobs for placeholders (colors, shapes)
- Additional tests for builders and error propagation
Have ideas? Open an issue or a PR.
Contributing #
PRs are welcome! Please run the checks before opening a PR:
dart format .
flutter analyze
flutter test
License #
MIT © DevsLoom — see LICENSE