empty_view
A powerful Flutter package for displaying beautiful empty state views with animations, Lottie support, presets, shimmer loading, dark mode, and accessibility features.
Features
- Smart Image Detection - Automatically detects and renders network images, local files, SVG assets, and regular assets
- Lottie Animation Support - Display beautiful Lottie animations in your empty states
- Entrance Animations - Smooth fade, slide, scale, and bounce animations
- Shimmer Skeleton Loading - Show a shimmer placeholder while content loads
- Dark Mode Support - Automatic theme detection and dark mode styling
- Accessibility - Built-in semantic labels for screen readers
- Icon Support - Use Material icons with optional circular backgrounds
- Secondary Action Button - Add a secondary action alongside the primary button
- Retry Logic - Built-in retry mechanism with configurable attempts and delays
- Platform Adaptive - Automatic Cupertino styling on iOS, Material on Android
- Prebuilt Presets - Ready-to-use empty states for common scenarios (16 total!)
- Highly Customizable - Extensive styling options via
EmptyViewStyle - Gradient Backgrounds - Beautiful gradient background support (NEW in v2.1)
- Builder Pattern - Custom layouts with
EmptyView.builder()(NEW in v2.1) - Animation Callbacks -
onAnimationCompletefor post-animation actions (NEW in v2.1) - Custom Error Widgets - Replace error icons with custom widgets (NEW in v2.1)
Installation
Add this to your package's pubspec.yaml file:
dependencies:
empty_view: ^2.1.0
Then run:
flutter pub get
Quick Start
import 'package:empty_view/empty_view.dart';
// Basic usage
EmptyView(
title: 'No Items Found',
description: 'There are no items to display at the moment.',
buttonText: 'Refresh',
onButtonTap: () => fetchItems(),
)
// Using presets (recommended for common scenarios)
EmptyViewPresets.noInternet(onRetry: () => checkConnection())
EmptyViewPresets.emptyCart(onShopNow: () => navigateToShop())
EmptyViewPresets.noSearchResults(searchTerm: 'shoes')
Usage Examples
Using Prebuilt Presets
The easiest way to use empty_view is with the prebuilt presets:
// No internet connection
EmptyViewPresets.noInternet(
onRetry: () => checkConnection(),
)
// Empty shopping cart
EmptyViewPresets.emptyCart(
onShopNow: () => navigateToShop(),
)
// No search results
EmptyViewPresets.noSearchResults(
searchTerm: 'flutter widgets',
onClearSearch: () => clearSearch(),
)
// Error state
EmptyViewPresets.error(
message: 'Failed to load data',
onRetry: () => fetchData(),
)
// Other available presets:
EmptyViewPresets.noNotifications()
EmptyViewPresets.noMessages(onNewMessage: () {})
EmptyViewPresets.noFavorites(onBrowse: () {})
EmptyViewPresets.noData(onRefresh: () {})
EmptyViewPresets.locationRequired(onEnableLocation: () {})
EmptyViewPresets.maintenance()
EmptyViewPresets.noOrders(onStartShopping: () {})
EmptyViewPresets.noFiles(onUpload: () {})
With Icon
EmptyView(
icon: Icons.inbox_outlined,
iconColor: Colors.grey,
iconSize: 80,
title: 'No Messages',
description: 'Your inbox is empty.',
buttonText: 'Compose',
onButtonTap: () => composeMessage(),
)
With Icon Background
EmptyView(
icon: Icons.cloud_off,
iconColor: Colors.blue,
iconSize: 80,
showIconBackground: true,
iconBackgroundColor: Colors.blue.shade50,
title: 'Offline Mode',
description: 'You are currently offline.',
)
With Lottie Animation
EmptyView(
lottiePath: 'assets/animations/empty.json', // or network URL
lottieRepeat: true,
title: 'Nothing Here',
description: 'Check back later for new content!',
buttonText: 'Refresh',
onButtonTap: () => refresh(),
)
With Entrance Animation
EmptyView(
icon: Icons.star,
title: 'Animated Entry',
description: 'Watch this smooth animation!',
enableAnimation: true,
animationType: EmptyViewAnimation.slideUp, // fadeIn, slideDown, scale, bounce
animationDuration: Duration(milliseconds: 800),
animationCurve: Curves.easeOutCubic,
)
Shimmer Skeleton Loading
// Using the factory constructor
EmptyView.skeleton()
// Or with custom colors
EmptyView.skeleton(
shimmerBaseColor: Colors.grey.shade300,
shimmerHighlightColor: Colors.grey.shade100,
)
// Toggle between shimmer and content
isLoading ? EmptyView.skeleton() : EmptyView(
icon: Icons.check,
title: 'Data Loaded',
description: 'Content is now available.',
)
With Secondary Button
EmptyView(
icon: Icons.error_outline,
title: 'Something Went Wrong',
description: 'We encountered an error loading your data.',
buttonText: 'Try Again',
onButtonTap: () => retry(),
secondaryButtonText: 'Contact Support',
onSecondaryButtonTap: () => openSupport(),
)
With Retry Logic
EmptyView(
icon: Icons.refresh,
title: 'Connection Failed',
description: 'Automatic retry with up to 3 attempts.',
onRetry: () async {
// This will be called automatically with retries
await fetchData();
},
buttonText: 'Retry',
maxRetries: 3,
retryDelay: Duration(seconds: 2),
)
Platform Adaptive Styling
EmptyView(
icon: Icons.phone_iphone,
title: 'Platform Adaptive',
description: 'Uses Cupertino on iOS, Material on Android.',
buttonText: 'Continue',
onButtonTap: () {},
adaptive: true, // Enables platform-specific styling
)
Custom Styling
EmptyView(
imagePath: 'https://example.com/image.png',
title: 'Fully Customizable',
description: 'Customize every aspect of the empty view.',
buttonText: 'Custom Button',
onButtonTap: () {},
style: EmptyViewStyle(
titleStyle: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.indigo,
),
descriptionStyle: TextStyle(
fontSize: 16,
color: Colors.grey,
),
buttonColor: Colors.indigo,
buttonTextColor: Colors.white,
buttonBorderRadius: 25,
buttonHeight: 56,
imageWidthFactor: 0.4,
imageHeightFactor: 0.25,
horizontalPadding: 32,
),
)
Using Style Presets
// Dark mode optimized
EmptyView(
description: 'Dark mode style',
style: EmptyViewStyle.dark(),
)
// Minimal spacing
EmptyView(
description: 'Minimal style',
style: EmptyViewStyle.minimal(),
)
// Compact for small spaces
EmptyView(
description: 'Compact style',
style: EmptyViewStyle.compact(),
)
// Spacious with more padding
EmptyView(
description: 'Spacious style',
style: EmptyViewStyle.spacious(),
)
// Pill-shaped buttons
EmptyView(
description: 'Rounded style',
style: EmptyViewStyle.rounded(),
)
Animation Types
| Animation | Description |
|---|---|
EmptyViewAnimation.none |
No animation |
EmptyViewAnimation.fadeIn |
Simple fade in effect |
EmptyViewAnimation.slideUp |
Slide up with fade |
EmptyViewAnimation.slideDown |
Slide down with fade |
EmptyViewAnimation.scale |
Scale up with fade |
EmptyViewAnimation.bounce |
Bounce effect with elastic curve |
EmptyView Properties
| Property | Type | Default | Description |
|---|---|---|---|
title |
String? |
- | Title text |
description |
String |
Required | Description text |
imagePath |
String? |
- | Path to image (auto-detected type) |
icon |
IconData? |
- | Icon to display |
iconColor |
Color? |
- | Icon color |
iconSize |
double |
80 | Icon size |
showIconBackground |
bool |
false | Show circular background for icon |
iconBackgroundColor |
Color? |
- | Icon background color |
lottiePath |
String? |
- | Path to Lottie animation |
lottieRepeat |
bool |
true | Repeat Lottie animation |
lottieReverse |
bool |
false | Reverse on repeat |
buttonText |
String? |
- | Primary button text |
onButtonTap |
VoidCallback? |
- | Primary button callback |
isLoading |
bool |
false | Show loading on button |
secondaryButtonText |
String? |
- | Secondary button text |
onSecondaryButtonTap |
VoidCallback? |
- | Secondary button callback |
isSecondaryLoading |
bool |
false | Show loading on secondary button |
onRetry |
Future<void> Function()? |
- | Retry callback with auto-retry |
maxRetries |
int |
3 | Maximum retry attempts |
retryDelay |
Duration |
2 seconds | Delay between retries |
enableAnimation |
bool |
true | Enable entrance animation |
animationType |
EmptyViewAnimation |
fadeIn | Animation type |
animationDuration |
Duration |
600ms | Animation duration |
animationCurve |
Curve |
easeOutCubic | Animation curve |
showShimmer |
bool |
false | Show shimmer skeleton |
shimmerBaseColor |
Color? |
- | Shimmer base color |
shimmerHighlightColor |
Color? |
- | Shimmer highlight color |
semanticLabel |
String? |
- | Accessibility label |
adaptive |
bool |
false | Use platform-adaptive styling |
style |
EmptyViewStyle? |
- | Custom style configuration |
customImage |
Widget? |
- | Custom image widget |
customButton |
Widget? |
- | Custom button widget |
errorIcon |
IconData |
broken_image | Icon when image fails |
errorIconSize |
double |
60 | Error icon size |
EmptyViewStyle Properties
| Property | Type | Default | Description |
|---|---|---|---|
titleStyle |
TextStyle? |
Theme default | Title text style |
descriptionStyle |
TextStyle? |
Theme default | Description text style |
titleColor |
Color? |
Theme color | Title color |
descriptionColor |
Color? |
Theme color | Description color |
buttonColor |
Color? |
Primary color | Button background color |
buttonTextColor |
Color? |
OnPrimary color | Button text color |
secondaryButtonColor |
Color? |
Primary color | Secondary button color |
secondaryButtonTextColor |
Color? |
- | Secondary button text color |
imageWidthFactor |
double |
0.5 | Image width as screen fraction |
imageHeightFactor |
double |
0.3 | Image height as screen fraction |
horizontalPadding |
double |
24.0 | Horizontal padding |
imageBottomSpacing |
double |
16.0 | Spacing below image |
titleBottomSpacing |
double |
12.0 | Spacing below title |
descriptionBottomSpacing |
double |
16.0 | Spacing below description |
buttonVerticalPadding |
double |
24.0 | Vertical padding around button |
buttonHorizontalPadding |
double |
32.0 | Horizontal padding around button |
buttonBorderRadius |
double |
12.0 | Button border radius |
buttonHeight |
double |
50.0 | Button height |
buttonSpacing |
double |
8.0 | Spacing between buttons |
buttonElevation |
double |
0.0 | Button elevation |
iconSizeFactor |
double |
1.0 | Icon size multiplier |
iconBackgroundColor |
Color? |
- | Icon container background |
iconContainerPadding |
double |
16.0 | Icon container padding |
Presets Reference
| Preset | Icon | Default Title |
|---|---|---|
noInternet |
wifi_off | No Internet Connection |
emptyCart |
shopping_cart | Your Cart is Empty |
noSearchResults |
search_off | No Results Found |
noNotifications |
notifications_off | No Notifications |
noMessages |
message | No Messages |
noFavorites |
favorite_border | No Favorites Yet |
error |
error_outline | Something Went Wrong |
noData |
inbox | No Data Available |
locationRequired |
location_off | Location Required |
maintenance |
construction | Under Maintenance |
noOrders |
receipt_long | No Orders Yet |
noFiles |
folder_off | No Files |
permissionDenied |
lock_outline | Permission Required (NEW) |
sessionExpired |
timer_off | Session Expired (NEW) |
emptyTimeline |
timeline | No Activity Yet (NEW) |
noPaymentMethods |
credit_card_off | No Payment Methods (NEW) |
Dark Mode
The package automatically detects dark mode and adjusts colors accordingly. You can also use the dark style preset:
EmptyView(
description: 'Optimized for dark mode',
style: EmptyViewStyle.dark(
buttonColor: Colors.tealAccent,
),
)
Accessibility
The package includes built-in accessibility support:
EmptyView(
title: 'Empty State',
description: 'No items available',
// Custom semantic label for screen readers
semanticLabel: 'The list is empty. Tap refresh to load items.',
buttonText: 'Refresh',
onButtonTap: () {},
)
New in v2.1
Gradient Backgrounds
EmptyView(
icon: Icons.star,
description: 'Beautiful gradient background',
style: EmptyViewStyle.gradient(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [Colors.purple.shade100, Colors.blue.shade100],
),
),
)
Builder Pattern for Custom Layouts
EmptyView.builder(
description: 'Custom layout with builder',
builder: (context, style) => Column(
children: [
Container(
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.blue.shade50,
shape: BoxShape.circle,
),
child: Icon(Icons.rocket_launch, size: 64, color: Colors.blue),
),
SizedBox(height: 16),
Text('Launch your app!', style: TextStyle(fontSize: 18)),
],
),
buttonText: 'Get Started',
onButtonTap: () {},
)
Animation Completion Callback
EmptyView(
icon: Icons.check_circle,
description: 'Animation completed!',
onAnimationComplete: () {
// Trigger actions after animation finishes
loadData();
},
)
Custom Error Widget
EmptyView(
imagePath: 'https://example.com/image.png',
description: 'With custom error widget',
errorWidget: Column(
children: [
Icon(Icons.cloud_off, size: 64, color: Colors.grey),
Text('Image unavailable'),
],
),
)
Migration Guide
From v1.x to v2.x
The package is fully backward compatible. All v1.x code works without changes:
// v1.x code still works in v2.x
EmptyView(
imagePath: 'assets/empty.png',
title: 'No Items',
description: 'Nothing to show.',
buttonText: 'Refresh',
onButtonTap: () {},
)
From v2.0 to v2.1
v2.1 is fully backward compatible with v2.0. New features are opt-in:
// v2.0 code works in v2.1
EmptyView(
icon: Icons.inbox,
title: 'No Items',
description: 'Nothing to show.',
enableAnimation: true,
animationType: EmptyViewAnimation.slideUp,
)
// New v2.1 features (optional)
EmptyView(
icon: Icons.inbox,
title: 'No Items',
description: 'Nothing to show.',
onAnimationComplete: () => print('Ready!'), // NEW
style: EmptyViewStyle(
backgroundGradient: LinearGradient(...), // NEW
),
)
Example
Check out the example folder for a complete sample app demonstrating all features.
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
License
MIT License - see the LICENSE file for details.
Libraries
- empty_view
- A powerful Flutter package for displaying beautiful empty state views with animations, Lottie support, presets, shimmer loading, dark mode, and accessibility features.