secure_gateway_image 0.0.2
secure_gateway_image: ^0.0.2 copied to clipboard
Error-resilient Flutter image widget with network, local cache, asset, and animated initials fallback support.
secure_gateway_image #
Error-resilient image fallback widget for Flutter.
Created and maintained by Rajat Das.
An error-resilient Flutter image widget with a deterministic fallback pipeline:
Network → Local Cache → Asset → Animated Initials Avatar
An error-resilient Flutter image widget with a deterministic fallback pipeline:
Network → Local Cache → Asset → Animated Initials Avatar
secure_gateway_image is designed for profile pictures, chat avatars, feed images, business pages, product images, and other UI components where a failed image source should never result in a broken-image UI.
✨ Features #
- 🌐 Network image as the primary source
- 💾 Local
ImageProviderfallback - 📦 Flutter asset fallback
- 👤 Animated initials avatar as the final fallback
- 🎨 Deterministic avatar colors
- ✨ Animated avatar entrance
- 🧩 Custom avatar builder
- ⏳ Loading placeholder support
- 🔐 Network request headers
- 🖼️ Image decoding controls
- ♿ Accessibility support
- 📡 Fallback stage callbacks
- 🌍 Flutter Web compatible
- 🪶 Zero third-party runtime dependencies
- 🚫 No state-management dependency
- 🔒 Null-safe
- 🧪 Testable and deterministic
📦 Installation #
Add the package to your pubspec.yaml:
dependencies:
secure_gateway_image: ^1.0.0
Then run:
flutter pub get
Import it:
import 'package:secure_gateway_image/secure_gateway_image.dart';
🚀 Basic Usage #
The simplest usage is:
SecureGatewayImage(
networkUrl: user.profileImageUrl,
initials: user.name,
size: 56,
)
The widget will try:
Network
↓ failure
Initials Avatar
🔥 Complete Fallback Pipeline #
The complete fallback order is:
┌─────────────────────┐
│ Network Image │
└──────────┬──────────┘
│
failure
↓
┌─────────────────────┐
│ Local Cache │
│ ImageProvider │
└──────────┬──────────┘
│
failure
↓
┌─────────────────────┐
│ Asset Image │
└──────────┬──────────┘
│
failure
↓
┌─────────────────────┐
│ Animated Initials │
│ Avatar │
└─────────────────────┘
For example:
SecureGatewayImage(
networkUrl: user.profileImageUrl,
cachedImage: FileImage(cachedFile),
assetPath: 'assets/images/default_avatar.png',
initials: user.name,
size: 64,
)
The behavior is:
networkUrl
↓
network succeeds
→ display network image
network fails
↓
cachedImage
↓
cache succeeds
→ display cached image
cache fails
↓
assetPath
↓
asset succeeds
→ display asset
asset fails
↓
initials
↓
display animated avatar
If a source isn't provided, it is automatically skipped.
💾 Local Cache #
secure_gateway_image intentionally does not manage disk caching.
The host application owns:
- HTTP caching
- disk storage
- cache expiration
- cache eviction
- stale-while-revalidate
- database storage
- encrypted storage
- authentication
- retry policies
The widget only receives an ImageProvider.
For example, with a cached File:
import 'dart:io';
SecureGatewayImage(
networkUrl: networkUrl,
cachedImage: FileImage(cachedFile),
assetPath: 'assets/images/default_avatar.png',
initials: 'Rajat Das',
)
You can also use a MemoryImage:
SecureGatewayImage(
networkUrl: networkUrl,
cachedImage: MemoryImage(imageBytes),
assetPath: 'assets/images/default_avatar.png',
initials: 'Rajat Das',
)
This design keeps caching concerns outside the UI package.
📦 Asset Fallback #
Provide an asset as the third fallback:
SecureGatewayImage(
networkUrl: networkUrl,
cachedImage: FileImage(cachedFile),
assetPath: 'assets/images/default_avatar.png',
initials: 'Rajat Das',
)
Make sure the asset is declared in your application's pubspec.yaml:
flutter:
assets:
- assets/images/default_avatar.png
👤 Initials Avatar #
If every image source fails, the widget generates an avatar from the supplied initials/name.
SecureGatewayImage(
initials: 'Rajat Das',
size: 64,
)
Produces:
┌──────────────┐
│ │
│ RD │
│ │
└──────────────┘
For:
initials: 'Rajat Das'
the generated initials are:
RD
For:
initials: 'Rajat'
the generated initials are:
R
For:
initials: ''
the generated fallback is:
?
🎨 Deterministic Avatar Colors #
Avatar colors are generated deterministically from the supplied name.
For example:
SecureGatewayImage(
initials: 'Rajat Das',
size: 64,
)
will consistently generate the same base avatar color for "Rajat Das".
This means the same user doesn't randomly change avatar colors every time the widget rebuilds.
✨ Avatar Animation #
The default initials avatar is animated.
SecureGatewayImage(
initials: 'Rajat Das',
size: 64,
animateAvatar: true,
)
Disable the animation if required:
SecureGatewayImage(
initials: 'Rajat Das',
size: 64,
animateAvatar: false,
)
Customize the animation:
SecureGatewayImage(
initials: 'Rajat Das',
size: 64,
animateAvatar: true,
avatarAnimationDuration:
const Duration(milliseconds: 700),
avatarAnimationCurve:
Curves.easeOutBack,
)
🧩 Custom Avatar #
If you don't want the built-in initials avatar, provide your own:
SecureGatewayImage(
initials: 'RD',
size: 64,
avatarBuilder: (
context,
initials,
) {
return ColoredBox(
color: Colors.black,
child: Center(
child: Text(
initials,
style: const TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
);
},
)
The custom builder is used only when all image sources fail.
⏳ Loading Placeholder #
You can provide a custom loading widget:
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
placeholder: (context) {
return const Center(
child: CircularProgressIndicator(),
);
},
)
For a skeleton:
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
placeholder: (context) {
return Container(
color: Colors.grey.shade300,
);
},
)
🔐 Network Headers #
Authenticated image endpoints can use headers:
SecureGatewayImage(
networkUrl: imageUrl,
headers: {
'Authorization': 'Bearer $token',
},
initials: 'RD',
)
This is useful for APIs where image URLs require authentication.
🖼️ Image Fit #
All standard Flutter BoxFit values are supported.
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
size: 100,
fit: BoxFit.cover,
)
Other options:
BoxFit.contain
BoxFit.cover
BoxFit.fill
BoxFit.fitWidth
BoxFit.fitHeight
BoxFit.none
BoxFit.scaleDown
📐 Width and Height #
You can use size:
SecureGatewayImage(
initials: 'RD',
size: 56,
)
or specify width and height independently:
SecureGatewayImage(
initials: 'RD',
width: 100,
height: 60,
)
If both width/height and size are supplied, explicit width and height take precedence.
🔲 Border Radius #
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
size: 64,
borderRadius:
BorderRadius.circular(16),
)
Circular avatar:
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
size: 64,
borderRadius:
BorderRadius.circular(999),
)
📡 Monitoring Fallback Stages #
You can monitor which source is currently active:
SecureGatewayImage(
networkUrl: imageUrl,
cachedImage: FileImage(cachedFile),
assetPath: 'assets/default.png',
initials: 'Rajat Das',
onStageChanged: (stage) {
debugPrint(
'Image stage: $stage',
);
},
)
Available stages:
GatewayImageStage.network
GatewayImageStage.cache
GatewayImageStage.asset
GatewayImageStage.avatar
For example, if the network fails and cache succeeds:
network
↓
cache
If everything fails:
network
↓
cache
↓
asset
↓
avatar
♿ Accessibility #
For meaningful images, provide a semantic label:
SecureGatewayImage(
networkUrl: profileImage,
initials: 'Rajat Das',
semanticLabel:
'Profile photo of Rajat Das',
)
For purely decorative images:
SecureGatewayImage(
networkUrl: backgroundImage,
initials: '',
excludeFromSemantics: true,
)
⚡ Performance #
For large source images displayed at small dimensions, use cacheWidth and cacheHeight.
Example:
SecureGatewayImage(
networkUrl: imageUrl,
initials: 'RD',
size: 48,
cacheWidth: 144,
cacheHeight: 144,
)
This can prevent unnecessarily decoding a huge original image when the UI only needs a small avatar.
Do not blindly use the original image resolution for tiny UI elements.
🌐 Flutter Web #
The package itself does not import dart:io.
The cache source is an ImageProvider, which means the widget can be used across Flutter platforms.
For mobile/desktop file caching:
SecureGatewayImage(
networkUrl: url,
cachedImage: FileImage(file),
initials: 'RD',
)
For web-compatible cached bytes:
SecureGatewayImage(
networkUrl: url,
cachedImage: MemoryImage(bytes),
initials: 'RD',
)
🏗️ Architecture #
The package intentionally follows a simple responsibility boundary.
┌─────────────────────────────────────┐
│ Application Layer │
│ │
│ API / Dio / Repository / Database │
│ │ │
│ │ cached image │
│ ▼ │
└─────────────┬───────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ SecureGatewayImage │
│ │
│ Network │
│ ↓ │
│ Cached ImageProvider │
│ ↓ │
│ Asset │
│ ↓ │
│ Initials Avatar │
└─────────────────────────────────────┘
The package does not own the application's data layer.
🚫 What This Package Does NOT Do #
secure_gateway_image does not provide:
- HTTP client
- Disk cache
- Database
- Authentication
- Token refresh
- Retry policy
- Cache eviction
- Background downloads
- State management
- Bloc
- Riverpod
- Provider
- GetX
- Service locator
These responsibilities should remain in the application layer.
🧠 Why No Built-in Cache? #
A common mistake would be to make this widget responsible for downloading and caching images.
That creates unnecessary coupling:
Widget
├── HTTP
├── Cache
├── Database
├── File system
├── Retry
└── Rendering
Instead, this package follows:
Repository
│
├── HTTP
├── Cache
└── Persistence
│
▼
SecureGatewayImage
│
├── Render network
├── Render cache
├── Render asset
└── Render avatar
This keeps the package small and reusable.
🧪 Testing #
Run:
flutter pub get
Format:
dart format .
Analyze:
flutter analyze
Run tests:
flutter test
Run all checks:
dart format --output=none --set-exit-if-changed .
flutter analyze
flutter test
📱 Example Application #
The repository contains an example application under:
example/
Run it:
cd example
flutter pub get
flutter run
📚 API Documentation #
Detailed API documentation is available at:
doc/api.md
The primary public API is:
SecureGatewayImage
and:
GatewayImageStage
📦 Publishing to pub.dev #
Before publishing:
flutter pub publish --dry-run
Review all warnings.
Then:
flutter pub publish
🔄 Versioning #
This package follows semantic versioning.
MAJOR.MINOR.PATCH
For example:
1.0.0
1.0.1
1.1.0
2.0.0
Use a patch release for backwards-compatible bug fixes.
Use a minor release for backwards-compatible features.
Use a major release for breaking API changes.
📝 Changelog #
See:
CHANGELOG.md
🤝 Contributing #
Contributions are welcome.
See:
CONTRIBUTING.md
🔒 Security #
See:
SECURITY.md
📄 License #
This project is licensed under the MIT License.
See:
LICENSE
⭐ Example #
A typical production usage might look like:
SecureGatewayImage(
networkUrl: user.profileImageUrl,
cachedImage: cachedFile == null
? null
: FileImage(cachedFile),
assetPath:
'assets/images/default_avatar.png',
initials: user.name,
size: 48,
borderRadius:
BorderRadius.circular(12),
fit: BoxFit.cover,
cacheWidth: 144,
cacheHeight: 144,
onStageChanged: (stage) {
debugPrint(
'Image source: $stage',
);
},
)
The resulting behavior is:
Network
│
┌──────┴──────┐
│ │
Success Failure
│ │
▼ ▼
Image Cache
│
┌──────┴──────┐
│ │
Success Failure
│ │
▼ ▼
Image Asset
│
┌──────┴──────┐
│ │
Success Failure
│ │
▼ ▼
Image Initials Avatar
The widget therefore provides a predictable rendering contract:
👨💻 Author #
Rajat Das
Flutter Developer | Mobile Application Developer
Created and maintained by Rajat Das.
GitHub: https://github.com/rajatdas_dev
📄 License #
Copyright © 2026 Rajat Das.
This project is licensed under the MIT License.
See LICENSE for details.
There should always be a valid visual representation, even when every external image source fails.