Cause
Inside the Android emulator, localhost (127.0.0.1) refers to the emulator itself, not your computer.
Base URLs by target
- Android emulator:
http://10.0.2.2:8000 (special alias for the host machine).
- iOS simulator:
http://localhost:8000 (it shares the host network).
- Physical device:
http://<your-computer-LAN-ip>:8000, both on the same Wi-Fi.
- Or with a USB device on Android:
adb reverse tcp:8000 tcp:8000, then use http://localhost:8000 on the device.
String get baseUrl {
const fromEnv = String.fromEnvironment('API_URL');
if (fromEnv.isNotEmpty) return fromEnv;
if (Platform.isAndroid) return 'http://10.0.2.2:8000';
return 'http://localhost:8000';
}
Pass it at run time: flutter run --dart-define=API_URL=http://192.168.1.20:8000.
Make the server listen on the right interface
Bind to all interfaces, otherwise other devices cannot connect: php artisan serve --host=0.0.0.0, or node / uvicorn --host 0.0.0.0.
Cleartext HTTP is blocked
Android 9+ and iOS (App Transport Security) block plain http:// by default for native networking, and Flutter applies the same policy on mobile: some requests can fail with "Insecure HTTP is not allowed by platform" or "Cleartext HTTP traffic not permitted". For development only, allow it in a debug-only manifest at android/app/src/debug/AndroidManifest.xml:
<application android:usesCleartextTraffic="true" />
(never ship this in release). On iOS use an NSExceptionDomains entry for your dev host in a debug-only plist. Use HTTPS in production.
Pitfalls
- Firewalls on the host machine can block port 8000 for LAN devices.
- Add the
INTERNET permission in the main manifest (the Flutter template only adds it to debug and profile).
dart:io Platform is unavailable on Flutter web; use kIsWeb first.