Application runs in debug mode but crashes in release mode why ?
Answer
Overview
A very common Flutter issue: the app works perfectly in debug mode but crashes in release mode. This happens because debug and release builds are compiled and optimized differently.
Key Differences: Debug vs Release
| Feature | Debug | Release |
|---|---|---|
| Compilation | JIT (Just-In-Time) | AOT (Ahead-Of-Time) |
| Optimization | None | Aggressive (R8/ProGuard) |
| Assertions | ✅ Enabled | ❌ Disabled |
| Observatory | ✅ Enabled | ❌ Disabled |
| Code shrinking | ❌ No | ✅ Yes (ProGuard/R8) |
| Obfuscation | ❌ No | ✅ Optional |
Common Causes & Fixes
1. ProGuard/R8 Shrinking removes needed classes
textError: MissingPluginException / ClassNotFoundException in release
proguard# android/app/proguard-rules.pro # Keep your model classes -keep class com.example.myapp.models.** { *; } # Keep Flutter-specific classes -keep class io.flutter.** { *; } -keep class io.flutter.plugins.** { *; } # If using Gson -keepattributes Signature -keepattributes *Annotation* -keep class com.google.gson.** { *; } # If using Retrofit -keepattributes *Annotation* -keep class retrofit2.** { *; }
2. dart:mirrors / Reflection not available in AOT
dart// ❌ dart:mirrors is NOT supported in Flutter release builds import 'dart:mirrors'; // Causes crash in release! // ✅ Use code generation instead (json_serializable, freezed) () class User { final String name; User({required this.name}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); }
3. assert() statements that crash
dart// assert() runs in debug but is REMOVED in release // If your logic depends on assert side-effects, extract it: // ❌ Bad — logic inside assert assert((value = computeValue()) != null); // ✅ Good — assert only for validation final value = computeValue(); assert(value != null, 'Value must not be null');
4. Missing permissions in release manifest
xml<!-- android/app/src/main/AndroidManifest.xml --> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" />
5. Debug vs Release API endpoints
dart// dart-define lets you switch environments const apiUrl = String.fromEnvironment( 'API_URL', defaultValue: 'https://api.prod.example.com ); // Build with: // flutter build apk --dart-define=API_URL=https://staging.example.com
Debugging Release Crashes
bash# Test release build locally first flutter run --release # Check release logs on Android adb logcat | grep flutter # Symbolicate crash stack traces (if obfuscated) flutter symbolize --input=crash_log.txt --debug-info=build/app.android-arm64.symbols
Release Mode Checklist
- Test with before submittingtext
flutter run --release - Check ProGuard rules for all third-party libraries
- Remove all usagetext
dart:mirrors - Verify all permissions are in text
AndroidManifest.xml - Check for iOS permission descriptionstext
Info.plist - Test on a physical device, not just emulator
- Verify API endpoints are production URLs
- Check for hardcoded debug credentials
Rule: Always test
on a real device before publishing. Most release crashes are caught immediately this way.textflutter run --release