safe_network_image 1.0.1

SDKflutter
Platformandroidioswindowslinuxmacosweb

A robust Flutter widget that extends cached_network_image with enhanced error handling, connectivity awareness, shimmer effects, and automatic retry functionality.

SafeNetworkImage

pub package style: flutter lints License: MIT

A robust Flutter widget that extends cached_network_image with enhanced error handling, connectivity awareness, shimmer effects, and automatic retry functionality.

Features

  • ✨ Shimmer Loading Animation: Beautiful animated placeholder while images load
  • 🔄 Automatic Retry: Configurable retry attempts with customizable delays
  • 📶 Connectivity Awareness: Detects network status and handles offline scenarios
  • 🎯 Flexible Fallbacks: Support for icon fallbacks or custom widgets
  • 🎨 Built-in Variants: Pre-configured avatar, card, and banner image types
  • ♿ Accessibility: Full semantic label support for screen readers
  • 👆 Touch Handling: Optional tap gesture handling
  • ⚡ Performance: Built on top of cached_network_image for optimal caching

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  safe_network_image: ^1.0.0

Then run:

flutter pub get

Quick Start

import 'package:flutter/material.dart';
import 'package:safe_network_image/safe_network_image.dart';

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SafeNetworkImage(
      url: 'https://example.com/image.jpg',
      fallback: Icons.person,
      width: 100,
      height: 100,
    );
  }
}

Usage Examples

Avatar Images

// Simple circular avatar
SafeNetworkImageBuilder.avatar(
  url: user.avatarUrl,
  radius: 24,
  fallback: Icons.person,
  onTap: () => navigateToProfile(),
)

// Large profile picture
SafeNetworkImageBuilder.avatar(
  url: user.avatarUrl,
  radius: 48,
)

Card Images

// Product card image
SafeNetworkImageBuilder.card(
  url: product.imageUrl,
  width: 200,
  height: 150,
  borderRadius: 12,
  onTap: () => viewProduct(),
)

// Square thumbnail
SafeNetworkImageBuilder.card(
  url: thumbnail.url,
  width: 100,
  height: 100,
)
// Hero banner
SafeNetworkImageBuilder.banner(
  url: article.heroImageUrl,
  height: 250,
  fallback: Icons.article,
)

// Full-width image
SafeNetworkImageBuilder.banner(
  url: banner.imageUrl,
  height: 200,
  onTap: () => handleBannerTap(),
)

Advanced Configuration

SafeNetworkImage(
  url: imageUrl,
  width: 200,
  height: 150,
  
  // Retry configuration
  maxRetries: 5,
  retryDelay: Duration(seconds: 2),
  
  // Shimmer customization
  showShimmer: true,
  shimmerBaseColor: Colors.grey[300],
  shimmerHighlightColor: Colors.grey[100],
  
  // Connectivity
  enableConnectivityCheck: true,
  
  // Fallback options
  fallback: Icons.image,
  fallbackWidget: CustomErrorWidget(),
  
  // Interaction
  onTap: () => handleImageTap(),
  semanticLabel: 'Product image',
  
  // Styling
  fit: BoxFit.cover,
  borderRadius: BorderRadius.circular(8),
)

Parameters

ParameterTypeDefaultDescription
urlString?requiredImage URL to load
fallbackIconData?Icons.imageIcon to show on error
fallbackWidgetWidget?nullCustom widget for error state
widthdouble?nullImage width
heightdouble?nullImage height
fitBoxFitBoxFit.coverHow image should fit container
borderRadiusBorderRadius?nullBorder radius for clipping
maxRetriesint3Maximum retry attempts
retryDelayDuration2 secondsDelay between retries
showShimmerbooltrueEnable shimmer loading effect
shimmerBaseColorColor?Colors.grey[300]Shimmer base color
shimmerHighlightColorColor?Colors.grey[100]Shimmer highlight color
semanticLabelString?nullAccessibility label
onTapVoidCallback?nullTap gesture handler
enableConnectivityCheckbooltrueMonitor network connectivity

Common Use Cases

E-commerce Product Grid

GridView.builder(
  itemBuilder: (context, index) {
    final product = products[index];
    return SafeNetworkImageBuilder.card(
      url: product.imageUrl,
      width: 150,
      height: 150,
      onTap: () => viewProduct(product),
    );
  },
)

User Profile Header

Row(
  children: [
    SafeNetworkImageBuilder.avatar(
      url: user.avatarUrl,
      radius: 32,
      onTap: () => editProfile(),
    ),
    SizedBox(width: 16),
    Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(user.name),
        Text(user.email),
      ],
    ),
  ],
)

Chat Message Avatars

SafeNetworkImage(
  url: message.senderAvatarUrl,
  width: 40,
  height: 40,
  borderRadius: BorderRadius.circular(20),
  fallback: Icons.person,
  semanticLabel: '${message.senderName} avatar',
)

States and Behaviors

Loading State

  • Shows animated shimmer effect by default
  • Customizable shimmer colors and animation
  • Can be disabled with showShimmer: false

Error State

  • Automatically retries failed loads up to maxRetries
  • Shows retry indicator during retry attempts
  • Falls back to icon or custom widget after max retries exceeded
  • Displays offline indicator when no network connectivity

Success State

  • Displays cached image with smooth transition
  • Stops shimmer animation automatically
  • Maintains proper aspect ratio and fit settings

Accessibility

SafeNetworkImage provides full accessibility support:

  • Semantic labels for screen readers
  • High contrast fallback options
  • Keyboard navigation support when tappable
  • Proper focus management

Performance Considerations

  • Images are cached automatically via cached_network_image
  • Shimmer animations are hardware-accelerated
  • Connectivity monitoring uses minimal resources
  • Retry logic prevents excessive network requests
  • Memory-efficient placeholder rendering

Migration from CachedNetworkImage

Replace existing CachedNetworkImage widgets:

// Before
CachedNetworkImage(
  imageUrl: user.avatarUrl,
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
  width: 100,
  height: 100,
)

// After
SafeNetworkImage(
  url: user.avatarUrl,
  fallback: Icons.person,
  width: 100,
  height: 100,
)

Contributing

Contributions are welcome! Please read our contributing guide and submit pull requests to our GitHub repository.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for a list of changes in each version.

Support