SmartFetch
A Flutter package that provides beautiful full-screen error pages and intelligent handling for server-side errors, no internet connection states, and timeouts. SmartFetch simplifies error handling in your Flutter applications with ready-to-use widgets and utilities.
Screenshots
No Internet

Server Error

Features
✨ Automatic Error Detection: Distinguishes between no internet, server errors, and timeouts
🎨 Beautiful Error Screens: Pre-built, animated full-screen error pages using Lottie animations
🔄 Easy Integration: Simple API with both programmatic and widget-based approaches
🎯 Type-Safe Results: Strongly-typed result objects for clean error handling
📱 Accessible Error Screens: Use predefined screens directly or via helper methods
🌐 Connectivity Aware: Real-time internet connectivity checking
🚀 Flexible Usage: Three ways to use error screens - automatic, helper methods, or direct access
Getting Started
Step 1: Add Dependency
Add smart_fetch to your pubspec.yaml:
dependencies:
smart_fetch: ^1.0.7
http: ^1.6.0
Step 2: Declare Assets (Required for Lottie Animations)
The package includes Lottie animations for error screens. You must declare the assets in your pubspec.yaml:
flutter:
assets:
- packages/smart_fetch/assets/animations/
Important: Without this declaration, the Lottie animations won't load and you'll see asset loading errors.
Step 3: Install
Run:
flutter pub get
Note: The assets are bundled with the package, so you only need to declare them in your app's pubspec.yaml - you don't need to copy the files.
Quick Start
Method 1: Automatic Error Screens (Easiest)
Error screens are shown automatically with SmartFetch.builder():
import 'package:smart_fetch/smart_fetch.dart';
import 'package:http/http.dart' as http;
SmartFetch.builder(
future: () => http.get(Uri.parse('https://api.example.com/data')),
onSuccess: (context, response) => Text('Success: ${response.body}'),
loadingWidget: CircularProgressIndicator(),
)
// Error screens show automatically!
Method 2: Using Helper Methods (Recommended)
Use SmartFetchNavigator for easy navigation to error screens:
import 'package:smart_fetch/smart_fetch.dart';
import 'package:http/http.dart' as http;
final result = await SmartFetch.call(
() => http.get(Uri.parse('https://api.example.com/data')),
);
// One line to show the correct error screen
SmartFetchNavigator.showErrorScreen(context, result);
// Or show specific screens
SmartFetchNavigator.showNoInternet(context);
SmartFetchNavigator.showServerError(context);
SmartFetchNavigator.showTimeout(context);
Method 3: Direct Screen Access
Import and use error screens directly:
import 'package:smart_fetch/smart_fetch.dart';
Navigator.push(
context,
MaterialPageRoute(builder: (_) => const NoInternetScreen()),
);
Usage Examples
Basic API Call with Error Handling
Option 1: When you have BuildContext (in a Widget)
import 'package:smart_fetch/smart_fetch.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
Future<void> fetchData(BuildContext context) async {
final result = await SmartFetch.call(
() => http.get(Uri.parse('https://api.example.com/data')),
timeout: const Duration(seconds: 10),
context: context,
errorSnackBar: const SnackBar(
content: Text('Request failed. Please try again.'),
backgroundColor: Colors.red,
),
);
if (result.isSuccess) {
print('Success: ${result.response?.body}');
} else {
// Use helper method - automatically shows correct screen
SmartFetchNavigator.showErrorScreen(context, result);
}
}
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => fetchData(context),
child: Text('Fetch Data'),
);
}
}
Option 2: When you don't have BuildContext (Service Classes)
Use a global navigator key and pass it to the helper methods:
// In main.dart
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey, // Add this
home: HomeScreen(),
);
}
}
// In your service class
import 'package:smart_fetch/smart_fetch.dart';
import 'package:http/http.dart' as http;
import 'main.dart'; // Import to access navigatorKey
class ApiService {
static Future<void> fetchData() async {
final result = await SmartFetch.call(
() => http.get(Uri.parse('https://api.example.com/data')),
);
if (result.isSuccess) {
print('Success: ${result.response?.body}');
} else {
// Use navigator key to show error screen
SmartFetchNavigator.showErrorScreenWithKey(navigatorKey, result);
}
}
}
Widget-Based Integration with Custom Error Handling
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:smart_fetch/smart_fetch.dart';
import 'package:http/http.dart' as http;
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
bool shouldLogin = false;
int loginTrigger = 0;
@override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: emailController,
decoration: const InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
labelText: 'Password',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 30),
if (!shouldLogin)
SizedBox(
width: double.infinity,
height: 50,
child: ElevatedButton(
onPressed: () {
setState(() {
shouldLogin = true;
loginTrigger++;
});
},
child: const Text('Login'),
),
)
else
SizedBox(
height: 200,
child: SmartFetch.builder(
key: ValueKey('login_fetch_$loginTrigger'),
future: () {
final body = {
"email": emailController.text,
"password": passwordController.text,
};
final headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
return http.post(
Uri.parse('https://api.example.com/login'),
headers: headers,
body: jsonEncode(body),
);
},
loadingWidget: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Logging in...'),
],
),
),
onSuccess: (context, response) {
if (response.statusCode == 200) {
// Handle successful login
return const Center(
child: Icon(
Icons.check_circle,
color: Colors.green,
size: 48,
),
);
} else {
// Handle error response
setState(() {
shouldLogin = false;
});
return Center(
child: Text('Login failed: ${response.statusCode}'),
);
}
},
timeout: const Duration(seconds: 10),
errorSnackBar: const SnackBar(
content: Text('Login failed. Please check your connection and try again.'),
backgroundColor: Colors.orange,
duration: Duration(seconds: 4),
),
onError: (context, error) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final errorScreen = error.isTimeout
? const TimeoutScreen()
: error.isNoInternet
? const NoInternetScreen()
: const ServerErrorScreen();
await Navigator.push(
context,
MaterialPageRoute(builder: (_) => errorScreen),
);
if (mounted) {
setState(() {
shouldLogin = false;
});
}
});
return const SizedBox.shrink();
},
),
),
],
),
),
);
}
}
Using Error Screens in Service Classes (No Context Needed!)
Perfect for service classes where you don't have BuildContext:
Step 1: Create navigator key in main.dart
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
home: HomeScreen(),
);
}
}
Step 2: Use in service class with navigator key
// In your service class
import 'main.dart'; // Import to access navigatorKey
final result = await SmartFetch.call(
() => http.post(Uri.parse(apiUrl), body: body),
);
if (result.isSuccess) {
// Handle success
} else {
// Show error screen using navigator key
SmartFetchNavigator.showErrorScreenWithKey(navigatorKey, result);
}
Manual Connectivity Check
import 'package:smart_fetch/smart_fetch.dart';
final hasInternet = await InternetChecker.hasInternet();
if (hasInternet) {
// Proceed with API call
} else {
SmartFetchNavigator.showNoInternet(context);
}
Asset Configuration
Lottie Animations
The package includes Lottie animations for error screens. You must declare the assets in your app's pubspec.yaml:
flutter:
assets:
- packages/smart_fetch/assets/animations/
Important Notes:
- Use the
packages/smart_fetch/prefix to access package assets - The assets are bundled with the package - you don't need to copy them
- After adding assets, run
flutter pub getand restart your app - If assets fail to load, the screens will show fallback icons automatically
Troubleshooting Asset Loading
If you see "Unable to load asset" or "Asset not found" errors:
-
Check your
pubspec.yaml- Make sure assets are declared:flutter: assets: - packages/smart_fetch/assets/animations/ -
Run
flutter pub getafter adding assets -
Restart your app completely (hot reload won't pick up asset changes)
-
Try
flutter cleanif issues persist:flutter clean flutter pub get flutter run
The screens include fallback icons if Lottie assets fail to load, so your app will still work even if there are asset issues.
Available Error Screens
The package provides three pre-built error screens:
NoInternetScreen- Shown when device has no internet connectivityServerErrorScreen- Shown when server returns an errorTimeoutScreen- Shown when request exceeds timeout duration
All screens include:
- Beautiful Lottie animations
- Clear error messages
- "Try Again" button for easy retry
API Reference
SmartFetch.call()
Makes an HTTP request with automatic error handling.
Parameters:
request: A function that returns a Future of http.Responsetimeout: Optional timeout duration (default: 10 seconds)context: Optional BuildContext to show error SnackBar when error occurserrorSnackBar: Optional SnackBar widget to show when error occurs (requires context)
Returns: SmartFetchResult with status and response data
SmartFetch.builder()
A widget builder that automatically handles loading, success, and error states.
Parameters:
key: Optional key for the FutureBuilder widget (useful for rebuilding)future: A function that returns a Future of http.ResponseonSuccess: Widget builder called when request succeedsloadingWidget: Optional custom loading widgettimeout: Optional timeout duration (default: 10 seconds)showErrorOnSamePage: If true, shows error screens inline on the same page. If false (default), uses onError callback to navigate to error screenonError: Callback called when an error occurs and showErrorOnSamePage is false (default behavior)errorSnackBar: Optional SnackBar widget to show when error occursnoInternetTitle: Optional custom title for no internet screennoInternetDescription: Optional custom description for no internet screentimeoutTitle: Optional custom title for timeout screentimeoutDescription: Optional custom description for timeout screenserverErrorTitle: Optional custom title for server error screenserverErrorDescription: Optional custom description for server error screen
SmartFetchNavigator
Helper class with static methods for navigating to error screens:
showNoInternet(context)- Show no internet screenshowServerError(context)- Show server error screenshowTimeout(context)- Show timeout screenshowErrorScreen(context, result)- Automatically show correct screenshowErrorScreenWithKey(navigatorKey, result)- Use with global navigator key
All methods support an optional replace parameter to replace the current route.
Error Screens
Direct access to error screen widgets:
NoInternetScreen()- No internet connection screenServerErrorScreen()- Server error screenTimeoutScreen()- Timeout screen
Example
Check out the complete example in the /example folder:
cd example
flutter run
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Uses Lottie for beautiful animations
- Uses connectivity_plus for network detection
- Built with ❤️ for the Flutter community
Libraries
- smart_fetch
- A Flutter package for handling network requests with beautiful error screens.