armor 1.0.0 copy "armor: ^1.0.0" to clipboard
armor: ^1.0.0 copied to clipboard

Bulletproof protection for Flutter apps. Automatically handles crashes, errors, and failures with graceful recovery.

πŸ›‘οΈ Armor #

Bulletproof protection for Flutter apps. Automatically handles crashes, errors, and failures with graceful recovery.

Pub Version License: MIT

✨ Features #

  • 🚫 Prevents crashes - No more red error screens
  • πŸ”„ Automatic recovery - Graceful fallbacks for failed operations
  • πŸ“ Render overflow protection - Eliminates yellow/black overflow stripes
  • 🌐 Network error handling - Retry logic with intelligent fallbacks
  • πŸ–ΌοΈ Safe image loading - Automatic placeholder and error widgets
  • ⚑ Performance monitoring - Built-in performance tracking
  • 🧹 Resource management - Automatic cleanup of timers and subscriptions
  • 🎯 Zero configuration - Works out of the box

πŸš€ Quick Start #

1. Add Armor to your pubspec.yaml #

dependencies:
  armor: ^1.0.0

2. Initialize Armor (one-time setup) #

import 'package:armor/armor.dart';

void main() {
  ArmorInterceptor.initialize(); // Enable global error protection
  runApp(MyApp());
}

3. Add ArmorMixin to your widgets #

import 'package:armor/armor.dart';

class MyWidget extends StatefulWidget {
  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> with ArmorMixin {
  // Your widget is now protected against all common Flutter errors!
  
  @override
  Widget build(BuildContext context) {
    return safeBuild(
      () => YourActualWidget(),
      fallback: Text('Content temporarily unavailable'),
    );
  }
}

That's it! Your app is now armored against crashes. πŸŽ‰

πŸ’‘ Core Methods #

safeExecute() - Execute any code safely #

final name = safeExecute(
  () => userData!['name'] as String,
  fallback: 'Unknown User',
  cacheKey: 'user_name',
);

safeSetState() - Prevent setState after dispose #

safeSetState(() {
  counter++;
});

safeAsync() - Safe async operations #

final result = await safeAsync(() async {
  final data = await api.fetchData();
  safeSetState(() => this.data = data);
  return data;
});

safeBuild() - Build widgets with fallback UI #

return safeBuild(
  () => ComplexWidget(data: complexData),
  fallback: Text('Content temporarily unavailable'),
);

armorImage() - Load images safely #

armorImage(
  'https://example.com/image.jpg',
  width: 100,
  height: 100,
  cacheKey: 'profile_image',
)

armorNetworkRequest() - Network calls with retry #

final data = await armorNetworkRequest(
  () => api.fetchUserData(),
  fallback: cachedData,
  maxRetries: 3,
  cacheKey: 'user_data',
);

armorTimer() - Managed timers #

armorTimer(
  Duration(seconds: 5),
  () => safeSetState(() => showMessage = false),
  key: 'hide_message_timer',
);

🎯 What Armor Protects Against #

Error Type Example Armor Solution
Null Safety userData!['name'] Returns fallback value
setState after dispose Timer callbacks Automatically prevented
Render Overflow Yellow/black stripes Responsive fallback layouts
Image Loading Broken image URLs Automatic placeholder widgets
Network Failures API timeouts Retry logic + cached fallbacks
Platform Errors Native method calls Graceful error handling
File Operations Permission denied Safe file access
Memory Leaks Undisposed timers Automatic resource cleanup

πŸ“Š Real-World Example #

Here's a complete example showing Armor protecting a user profile widget:

class UserProfile extends StatefulWidget {
  final Map<String, dynamic>? userData;
  
  const UserProfile({Key? key, this.userData}) : super(key: key);
  
  @override
  State<UserProfile> createState() => _UserProfileState();
}

class _UserProfileState extends State<UserProfile> with ArmorMixin {
  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(16),
        child: safeBuild(
          () => _buildUserContent(),
          fallback: _buildPlaceholder(),
        ),
      ),
    );
  }
  
  Widget _buildUserContent() {
    // These would normally crash with null data, but Armor provides fallbacks
    final name = safeExecute(
      () => widget.userData!['name'] as String,
      fallback: 'Unknown User',
      cacheKey: 'user_name',
    );
    
    final email = safeExecute(
      () => widget.userData!['email'] as String,
      fallback: 'no-email@example.com',
      cacheKey: 'user_email',
    );
    
    return Column(
      children: [
        armorImage(
          widget.userData?['avatar_url'] ?? '',
          width: 80,
          height: 80,
          cacheKey: 'user_avatar',
        ),
        SizedBox(height: 16),
        Text(name!, style: Theme.of(context).textTheme.headlineSmall),
        Text(email!, style: Theme.of(context).textTheme.bodyMedium),
      ],
    );
  }
  
  Widget _buildPlaceholder() {
    return Column(
      children: [
        CircleAvatar(
          radius: 40,
          backgroundColor: Colors.grey[300],
          child: Icon(Icons.person, size: 40),
        ),
        SizedBox(height: 16),
        Container(
          height: 20,
          width: 120,
          decoration: BoxDecoration(
            color: Colors.grey[300],
            borderRadius: BorderRadius.circular(4),
          ),
        ),
      ],
    );
  }
}

πŸ“ˆ Monitoring & Analytics #

Armor provides built-in monitoring to track protection effectiveness:

// Get protection statistics
final stats = getArmorStats();
print('Cached values: ${stats['cached_values']}');
print('Active timers: ${stats['active_timers']}');

// Monitor global error interception
final interceptor = ArmorInterceptor.instance;
print('Total errors intercepted: ${interceptor.totalIntercepted}');
print('Total errors healed: ${interceptor.totalHealed}');
print('Heal rate: ${(interceptor.healRate * 100).toStringAsFixed(1)}%');

// Listen to error stream for analytics
ArmorInterceptor.instance.errorStream.listen((error) {
  // Send to your analytics service
  analytics.recordError(error);
});

πŸ”§ Advanced Configuration #

Custom Error Handling #

// Listen to all intercepted errors
ArmorInterceptor.instance.errorStream.listen((error) {
  if (error.wasHealed) {
    print('βœ… Armor healed: ${error.error}');
  } else {
    print('❌ Unhealed error: ${error.error}');
  }
});

Performance Monitoring #

final result = armorPerformanceOperation(
  () => expensiveComputation(),
  operationName: 'data_processing',
  warningThreshold: Duration(milliseconds: 500),
);

Cache Management #

// Clear cache when needed (e.g., user logout)
clearArmorCache();

// Check cache size
final stats = getArmorStats();
if (stats['cached_values'] > 1000) {
  clearArmorCache();
}

πŸ—οΈ Architecture #

Armor consists of three main components:

  1. ArmorMixin - The main protection system for widgets
  2. ArmorInterceptor - Global error catching and healing
  3. ArmorRegistry - Tracking and statistics for healed errors
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Your Widget   β”‚    β”‚   ArmorMixin     β”‚    β”‚ ArmorInterceptorβ”‚
β”‚                 │───▢│  (Protection)    │───▢│ (Global Errors) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                β”‚                        β”‚
                                β–Ό                        β–Ό
                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                       β”‚ Safe Operations  β”‚    β”‚ ArmorRegistry   β”‚
                       β”‚ (Fallbacks)      β”‚    β”‚ (Statistics)    β”‚
                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ§ͺ Testing #

Armor includes comprehensive test coverage and provides utilities for testing:

// Test your armored widgets
testWidgets('should recover from null data error', (tester) async {
  await tester.pumpWidget(
    MaterialApp(
      home: UserProfile(userData: null), // Null data
    ),
  );
  
  // Should show placeholder instead of crashing
  expect(find.byIcon(Icons.person), findsOneWidget);
});

🚦 Migration Guide #

From Manual Error Handling #

// Before: Manual try-catch everywhere
try {
  setState(() => counter++);
} catch (e) {
  print('Error: $e');
}

// After: Armor handles it automatically
safeSetState(() => counter++);

From Other Error Packages #

// Before: Complex setup
SomeErrorPackage.configure(/* lots of config */);

// After: One line setup
ArmorInterceptor.initialize();

πŸ“ Best Practices #

  1. Always use ArmorMixin for StatefulWidgets
  2. Provide meaningful fallbacks instead of generic error messages
  3. Use cache keys for expensive operations
  4. Monitor error statistics to identify problem areas
  5. Clear cache periodically to prevent memory growth

❓ FAQ #

Q: Does Armor add performance overhead? A: Minimal. Armor uses efficient error handling and caching strategies. Performance monitoring is built-in to detect any issues.

Q: Can I use Armor with other error handling packages? A: Yes! Armor works alongside other packages and can complement existing error handling.

Q: What happens to errors that can't be healed? A: They're logged for monitoring but won't crash your app. In debug mode, you'll still see them in the console.

Q: Is Armor production-ready? A: Absolutely! Armor is designed for production use with comprehensive testing and proven patterns.

🀝 Contributing #

We welcome contributions! Please see our Contributing Guide for details.

πŸ“„ License #

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


Made with ❀️ for the Flutter community

Armor up your Flutter apps! πŸ›‘οΈ

51
likes
160
points
51
downloads

Publisher

verified publishercodepur.dev

Weekly Downloads

Bulletproof protection for Flutter apps. Automatically handles crashes, errors, and failures with graceful recovery.

Repository (GitHub)
View/report issues

Topics

#error-handling #crash-prevention #resilience #flutter-widgets #error-recovery

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on armor