Your Flutter application works perfectly when you run:

flutter run

API calls work, login succeeds, Firebase connects, images load and navigation behaves normally.

Then you create a release build:

flutter build apk --release

or:

flutter build appbundle

and suddenly something breaks.

The app may open but fail to connect to the API. Login may stop working. Google Sign-In may fail. Firebase notifications may disappear. Images may not load. In some cases, the release build may crash immediately after launch.

This can be confusing because the same Flutter code works correctly in debug mode.

Usually, however, there is a specific difference between your development and production environments. This guide walks through the most common causes and shows how to find the real problem instead of randomly changing your Flutter code.

Why Does a Flutter App Work in Debug but Not Release Mode?

Debug and release builds are not identical.

A debug build is designed for development. It includes debugging support and is generally more forgiving while you’re testing locally.

A release build is optimized for distribution.

The environment can also change:

Debug
Flutter App
   ↓
Development Configuration
   ↓
Local/Test API
   ↓
Debug Firebase / OAuth Configuration

while production may use:

Release
Flutter App
   ↓
Production Configuration
   ↓
HTTPS Production API
   ↓
Release Firebase / OAuth Configuration

That means the problem may not actually be Flutter itself.

It may be caused by Android permissions, API configuration, SSL, environment variables, Firebase setup, signing certificates or release optimization.

The first step is identifying what specifically stops working.

1. Test the Release Build Locally

Don’t wait until the app reaches Google Play before testing release behaviour.

Build a release APK:

flutter build apk --release

The APK is normally generated under:

build/app/outputs/flutter-apk/app-release.apk

Install it on a real Android device and test the application.

You can also run Flutter in release mode on a connected supported device:

flutter run --release

This helps answer an important question:

Is the problem caused by release mode itself, or does it only happen after distribution through Google Play?

Those are different troubleshooting paths.

2. Check Android Internet Permission

One of the first things to check when APIs work during development but fail in a release Android app is internet permission.

Open:

android/app/src/main/AndroidManifest.xml

Make sure the application has:

<uses-permission android:name="android.permission.INTERNET" />

It should be placed inside the <manifest> element, not inside <application>.

For example:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:label="My App"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">

        ...
        
    </application>
</manifest>

If your app depends on a remote REST API, this is an essential configuration to verify.

3. Check Whether Your API Still Uses localhost

This is an extremely common development mistake.

During development, your API URL might be:

const baseUrl = 'http://localhost:8000/api';

But inside an Android application, localhost refers to the Android device itself.

It does not mean your development computer or production server.

You may also have something such as:

const baseUrl = 'http://192.168.1.10:8000/api';

That may work while your phone and computer are connected to the same Wi-Fi network.

Once the app is installed somewhere else, that local IP is useless.

Your production build should use your actual production API, for example:

const baseUrl = 'https://api.example.com/api';

Before looking for complicated Flutter problems, verify the final API URL being used by the release build.

4. Check HTTP vs HTTPS

Suppose your Flutter application calls:

http://api.example.com

instead of:

https://api.example.com

Modern Android security policies can restrict cleartext HTTP traffic.

Your debug environment may make the problem less obvious, especially if you have different manifests or development configurations.

For production applications, prefer a properly configured HTTPS API.

Don’t disable Android security globally just to make an insecure production API work unless you have a specific controlled requirement.

5. Check the API SSL Certificate

Using HTTPS isn’t enough if the SSL configuration is invalid.

Test your production API URL directly.

For example:

https://api.example.com/api

Look for:

  • Expired certificate
  • Invalid certificate chain
  • Hostname mismatch
  • Incorrect redirects
  • Misconfigured intermediate certificate

A browser on your development computer may sometimes hide or handle certificate issues differently from an Android application.

Avoid solving production SSL problems by bypassing certificate validation in your Flutter code.

Fix the server certificate instead.

6. Check Your Environment Configuration

Many Flutter projects use different configurations for development and production.

For example:

const String apiUrl = String.fromEnvironment(
  'API_URL',
  defaultValue: 'https://api.example.com',
);

Then you might build using:

flutter build apk \
  --release \
  --dart-define=API_URL=https://api.example.com

If you forget the --dart-define, your release build may use the wrong value.

Check every environment-specific value

This may include:

  • API base URL
  • WebSocket URL
  • Firebase configuration
  • OAuth client IDs
  • Payment environment
  • Feature flags
  • App environment
  • CDN URL

Do not assume that because the debug configuration is correct, the release configuration is also correct.

7. Never Store Important Secrets in a Flutter App

A related mistake is putting sensitive server secrets directly into Flutter code.

For example, don’t treat this as secure:

const apiSecret = 'MY_PRIVATE_SERVER_SECRET';

A compiled mobile application is distributed to users and should not be considered a secure place for server-side secrets.

Keep sensitive credentials on your backend.

Your Flutter application should normally authenticate with your API using an appropriate user/session/token flow rather than embedding privileged backend secrets.

8. Check Dio or HTTP API Errors Properly

Sometimes the API isn’t “not working.”

The application simply isn’t showing you the actual error.

If you’re using Dio, don’t reduce every exception to:

catch (e) {
  print('Something went wrong');
}

During controlled troubleshooting, inspect useful information such as:

try {
  final response = await dio.get('/profile');
  print(response.data);
} on DioException catch (e) {
  print('Type: ${e.type}');
  print('Message: ${e.message}');
  print('Status: ${e.response?.statusCode}');
  print('Response: ${e.response?.data}');
}

This can distinguish between:

401 Unauthorized
403 Forbidden
404 Not Found
500 Internal Server Error

and an actual connection failure.

That distinction matters.

9. Check Whether Authentication Tokens Are Available

A release-only login problem may actually be a session-storage problem.

Suppose login succeeds and you save a token.

Later:

final token = await storage.read(key: 'auth_token');

If the token isn’t available when the next API request is made, the backend may return:

401 Unauthorized

Check the complete flow:

Login
  ↓
Receive Token
  ↓
Store Token
  ↓
Open Dashboard
  ↓
Read Token
  ↓
Attach Authorization Header
  ↓
Call API

A failure anywhere in this chain can look like “release mode API not working.”

10. Check Your Authorization Header

For token-based APIs, confirm that the release app actually sends the required header.

For example:

options.headers['Authorization'] = 'Bearer $token';

Also check your Dio interceptors.

If the interceptor behaves differently because of environment initialization or session timing, your requests may reach production without authentication.

The server logs can be extremely useful here.

11. Check Production API Server Logs

Don’t troubleshoot only from Flutter.

If the release application sends a request to your backend, check the backend logs too.

For Laravel, for example:

storage/logs/laravel.log

Your mobile app may show:

Something went wrong

while Laravel records the actual cause:

Unauthenticated.

or:

SQLSTATE...

or:

Route not found...

Mobile logs tell you what the app sees.

Server logs tell you what the backend sees.

You often need both.

12. Check API Redirects

Production servers sometimes redirect requests.

For example:

http://api.example.com
        ↓
https://api.example.com

or:

https://example.com/api
        ↓
https://www.example.com/api

Authentication headers or request behaviour can be affected by poorly configured redirect chains.

Use the final HTTPS API URL directly wherever possible instead of depending on unnecessary redirects.

13. Check Firebase Configuration

Firebase-related features can work in debug builds and fail in release builds if Android/Firebase configuration is incomplete.

Depending on the Firebase service you’re using, verify:

  • Android package name
  • google-services.json
  • Firebase Android app
  • SHA fingerprints
  • Firebase project
  • Release signing configuration

The package name registered with Firebase should match the actual Android application.

14. Check SHA-1 and SHA-256 Fingerprints

This is particularly important for services such as Google Sign-In and some Firebase integrations.

Your debug certificate and release certificate have different fingerprints.

You may have configured Firebase using only the debug SHA fingerprint.

Then:

Debug Build → Works
Release Build → Authentication Fails

Get the relevant signing certificate fingerprints and ensure the required release fingerprints are configured with the appropriate service.

After changing Firebase Android configuration, you may also need an updated google-services.json, depending on the change/service involved.

15. Google Sign-In Works in Debug but Not Release

This is one of the classic release-only problems.

If Google Sign-In works during development but fails in the signed application, investigate the signing configuration first.

Check the package name

It must match your configured Android application.

Check the release SHA fingerprints

The certificate signing the distributed application matters.

Check Google Play App Signing

When using Google Play App Signing, the certificate used for builds delivered by Google Play may differ from your local upload key.

That means a locally installed release APK might work while the Play Store version fails—or vice versa.

Check the certificates associated with your Play app and configure the relevant fingerprints with Google/Firebase as required.

16. Check Firebase Cloud Messaging

If push notifications work in debug but not after release, verify more than your Dart notification code.

Check:

  • Firebase project
  • Package/application ID
  • FCM token generation
  • Token being sent to your backend
  • Notification permission where applicable
  • Android notification channel
  • Background handler configuration
  • Backend sending to the correct token/project
  • Release Firebase configuration

Also remember that a reinstall can result in a different registration token.

Your backend should handle token updates properly.

17. Check Android Release Signing

Release applications are signed.

Your signing configuration may affect external integrations that identify your application using its certificate.

Review:

android/app/build.gradle

or the equivalent Gradle Kotlin configuration if your project uses it.

Make sure the correct release signing configuration is being used.

Do not commit private keystore passwords or sensitive signing material to a public repository.

18. Check ProGuard and R8

Release builds may use optimization, shrinking or obfuscation depending on your Android configuration.

If a library relies on classes that are removed or renamed unexpectedly, a feature may fail only in release.

If your release configuration enables minification and the problem appears only there, test whether optimization is involved.

For example, inspect your release configuration and plugin documentation for required keep rules.

Don’t solve the problem by permanently disabling all optimization without understanding the cause.

If a dependency requires ProGuard/R8 rules, configure the appropriate rules.

19. Check Tree Shaking and Dynamic Behaviour

Flutter release builds perform optimizations.

Most normal Flutter code handles this automatically, but some packages or patterns involving reflection-like/dynamic behaviour, generated code or platform integration may require special configuration.

If one specific package works in debug and fails in release:

  1. Check the package documentation.
  2. Check its release-mode setup.
  3. Verify generated files.
  4. Check Android configuration.
  5. Test the latest compatible package version.

Don’t assume Flutter itself is the problem just because the failure appears in release mode.

20. Check Assets and File Names

An asset may work during development but fail after deployment because of an incorrect path or case mismatch.

Suppose your file is:

assets/images/Logo.png

but your code references:

Image.asset('assets/images/logo.png');

Case sensitivity can create unexpected behaviour across environments and tooling.

Also verify pubspec.yaml:

flutter:
  assets:
    - assets/images/

After changing assets, run:

flutter clean
flutter pub get

and rebuild.

21. Check Native Plugin Configuration

Flutter packages that integrate with Android often require native configuration.

Examples include:

  • Google Maps
  • Firebase
  • Camera
  • Location
  • Notifications
  • OAuth
  • Payment SDKs
  • Deep links

Installing the Dart package may not be enough.

Check whether the package requires changes to:

AndroidManifest.xml
build.gradle
proguard-rules.pro
google-services.json

or other Android resources.

Always review the package’s current Android installation instructions.

22. Check Android Permissions

Some features require runtime or manifest permissions.

Examples include:

  • Camera
  • Location
  • Notifications
  • Bluetooth
  • Storage/media access

Don’t assume permissions are correct because the feature worked on one development device.

Test a clean installation of the release build on another device where permissions have never previously been granted.

23. Test a Clean Installation

Development devices accumulate state.

They may already contain:

  • Old authentication tokens
  • Granted permissions
  • Cached data
  • Previous Firebase tokens
  • Old app configuration

Uninstall the app completely.

Then install the release build again.

This gives you a better approximation of a new user’s experience.

A clean-install test can expose initialization and permission problems that are hidden on your development device.

24. Check SharedPreferences and Secure Storage

If your app depends on stored configuration, verify what happens when no stored data exists.

Avoid code that assumes a value will always be available.

For example, instead of blindly expecting:

final token = prefs.getString('token')!;

handle a missing token safely:

final token = prefs.getString('token');

if (token == null || token.isEmpty) {
  // Redirect to login or restore the session appropriately.
}

Release testing should include both:

Existing user with stored session

and:

Fresh installation with no session

25. Check App Initialization Order

Release mode can expose timing assumptions that weren’t obvious during development.

For example:

App starts
   ↓
Read token
   ↓
Initialize API client
   ↓
Validate session
   ↓
Load dashboard

If the dashboard starts loading before authentication initialization finishes, you may get:

Login → Dashboard → Unauthorized → Login

or other inconsistent navigation.

Make initialization explicit rather than depending on accidental timing.

26. Check Release-Only Crashes

If the application immediately closes, inspect device logs.

For Android, adb logcat can provide important information.

For example:

adb logcat

You can filter or inspect logs around the crash to identify native exceptions, plugin failures or Android configuration issues.

A release app that “just closes” usually leaves evidence in the device logs.

27. Don’t Depend Only on print()

Developers often rely heavily on:

print('API called');

during debugging.

Production/release diagnostics need a more deliberate strategy.

For important production applications, consider appropriate crash/error monitoring and structured logging while avoiding sensitive customer data, credentials and authentication tokens.

The objective is to know:

What failed?

On which app version?

On which platform/device?

At which stage?

without exposing private information.

28. Check the App Version Being Tested

This sounds obvious, but it causes real confusion.

You fix a problem, build again, and then accidentally test an older APK.

Or Google Play still serves a different release track/version than expected.

Check:

version: 1.2.0+15

in pubspec.yaml.

Understand:

  • Version name
  • Build number/version code
  • Play Store release track
  • Installed version

Make sure the device is actually running the build you think it is.

29. Run flutter doctor

Run:

flutter doctor

Fix relevant environment problems before assuming the application code is responsible.

Also check your Flutter version:

flutter --version

and dependency state:

flutter pub outdated

Don’t blindly upgrade every package on a production project, but know what environment you’re building with.

30. Clean and Rebuild the Project

Build caches can occasionally complicate troubleshooting.

A common controlled rebuild sequence is:

flutter clean
flutter pub get
flutter build apk --release

For an Android App Bundle:

flutter clean
flutter pub get
flutter build appbundle

This isn’t a universal solution, but it helps ensure you’re testing a fresh build after configuration changes.

A Real-World Example: API Works in Debug but Not Release

Suppose your Flutter app communicates with a Laravel API.

Debug login works.

The release APK shows:

Unable to connect to server

Step 1 — Check the production API URL

The release configuration contains:

http://192.168.1.5:8000/api

That’s the developer’s local computer.

Step 2 — Replace it with production API

For example:

https://api.example.com/api

Step 3 — Verify HTTPS

The production SSL certificate is valid.

Step 4 — Rebuild

flutter clean
flutter pub get
flutter build apk --release

Now the release application can communicate with the production server.

The problem wasn’t Dio, Flutter or Laravel.

It was simply an environment-specific API URL.

A Real-World Example: Google Sign-In Fails After Play Store Release

Suppose:

Debug APK → Google Sign-In works
Local release APK → Google Sign-In works
Google Play version → Google Sign-In fails

This pattern immediately gives us useful information.

Step 1 — Compare signing certificates

The Google Play distributed build may be signed with the Google Play App Signing certificate.

Step 2 — Check the required fingerprint configuration

Verify the relevant SHA fingerprint from the Play Console against the Firebase/Google configuration used by the app.

Step 3 — Update configuration if required

Add the appropriate signing fingerprint and update the Android/Firebase configuration according to the service’s requirements.

The Flutter login code may require no change at all.

The difference is how the application is signed after distribution.

A Real-World Example: Login Works but Dashboard Returns to Login

Another common situation is:

Login
  ↓
API returns token
  ↓
Dashboard opens
  ↓
Dashboard API returns 401
  ↓
App redirects to Login

This can look like a navigation problem.

But the real question is:

Was the authentication token attached to the dashboard request?

Check:

  1. Token returned by login.
  2. Token stored successfully.
  3. Token available when API client initializes.
  4. Authorization: Bearer ... attached.
  5. Backend accepts the token.
  6. Session validation doesn’t clear the token prematurely.

Tracing the authentication lifecycle is more effective than repeatedly changing navigation code.

Flutter Release Mode Troubleshooting Checklist

When your Flutter app works in debug but not release, check:

  • Release build tested locally
  • Android internet permission
  • Production API URL
  • No localhost/local-network API
  • HTTPS enabled
  • SSL certificate valid
  • Environment variables
  • --dart-define values
  • Dio/HTTP errors
  • Authentication token storage
  • Authorization headers
  • Backend logs
  • API redirects
  • Firebase project configuration
  • Package/application ID
  • SHA-1/SHA-256 fingerprints
  • Google Play App Signing
  • Google Sign-In configuration
  • FCM configuration
  • Android release signing
  • R8/ProGuard
  • Asset paths
  • pubspec.yaml
  • Native plugin setup
  • Android permissions
  • Clean installation
  • SharedPreferences/secure storage
  • App initialization order
  • Device crash logs
  • App version/build number
  • Flutter environment
  • Fresh clean build

Don’t change five things at once.

Find the stage where debug and release behaviour becomes different.

How to Prevent Flutter Release Problems

Test Release Mode During Development

Don’t wait until your application is ready for Google Play.

Periodically run:

flutter run --release

and create actual release builds.

Keep Development and Production Configuration Separate

Use a predictable configuration strategy for:

  • API URLs
  • Firebase
  • Feature flags
  • Payment environments
  • OAuth

This reduces the chance of accidentally shipping development settings.

Use HTTPS From the Beginning

If your application will eventually communicate with a production API, configure proper HTTPS early.

This prevents last-minute Android network-security surprises.

Test Fresh Installations

Test your app as a completely new user.

Don’t rely only on a development phone that has months of cached application state.

Test Signed Builds

Features involving Firebase, OAuth or external SDKs should be tested using the relevant release signing configuration.

Monitor Backend Errors Too

Flutter is only one half of an API-driven mobile application.

When production fails, check both:

Flutter logs + Backend logs

Create a Release Checklist

Before uploading an AAB, verify:

Production API
HTTPS
App version
Firebase
Signing
Authentication
Notifications
Critical API calls
Login/logout
Fresh installation
Release build

A 10-minute checklist can prevent a failed production release.

Final Thoughts

When a Flutter app works in debug but not release mode, don’t immediately rewrite working Dart code.

First identify what changed between the two environments.

The problem is often somewhere in this chain:

Flutter Release Build
        ↓
Android Configuration
        ↓
Environment / API URL
        ↓
HTTPS / SSL
        ↓
Authentication
        ↓
Firebase / OAuth
        ↓
Signing
        ↓
Production Backend

Test the release build early, inspect the actual API response, check device and backend logs, and verify release-specific credentials and signing configuration.

Once you know exactly where the behaviour changes, the problem usually becomes much easier to solve.

Need Flutter App Help?

Need Help With a Flutter Release or API Integration?

KDP Infusion can help troubleshoot Flutter release builds, REST API integrations, authentication, Firebase configuration, Google Sign-In, notifications and Flutter applications connected to Laravel, WordPress or WooCommerce backends.

  • Flutter application development
  • API integration and troubleshooting
  • Authentication and session issues
  • Firebase and push notifications
  • Android and iOS deployment support
Discuss Your Flutter App →