I am implementing a login feature in flutter using Express. I have created a login API which works absolutely fine on Postman. When correct credentials are provided a token is generated and returned.
But when I use the same API in flutter, the token returns null.
The following is my authService.dart file :
// services/auth_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:manunited_trivia/models/User.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final String baseUrl =
"http://192.168.1.6:8080/api/auth";
Future<String?> register(String email, String password) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/register'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'email': email, 'password': password}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['token'];
} else {
return null;
}
} catch (e) {
print('Error: $e');
return null;
}
}
Future<String?> login(String email, String password) async {
try {
final response = await http.post(
Uri.parse('$baseUrl/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'email': email, 'password': password}),
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return data['token'];
} else {
return null;
}
} catch (e) {
print('Error: $e');
return null;
}
}
Future<User?> getUser(String token) async {
try {
final response = await http.get(
Uri.parse('$baseUrl/user'),
headers: {
'Content-Type': 'application/json',
'x-auth-token': token,
},
);
if (response.statusCode == 200) {
final data = jsonDecode(response.body);
return User.fromJson(data);
} else {
return null;
}
} catch (e) {
print('Error: $e');
return null;
}
}
}
And the following is my Login Page :
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:manunited_trivia/constants/ColorsToUse.dart';
import 'package:manunited_trivia/screens/RegisterScreen.dart';
import 'package:manunited_trivia/services/auth_service.dart';
import 'package:quickalert/models/quickalert_type.dart';
import 'package:quickalert/widgets/quickalert_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool obscure = true;
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
final AuthService authService = AuthService();
void _login(BuildContext context) async {
String username = _emailController.text.trim();
String password = _passwordController.text.trim();
String? token = await authService.login(username, password);
if (token != null) {
// Store token in shared preferences for persistent login
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('token', token);
// Navigate to home screen
Navigator.pushReplacementNamed(context, '/homeScreen');
} else {
print(token);
// Handle login failure
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Login failed')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white, // Background color
body: Stack(
children: <Widget>[
Image.asset(
'assets/images/header_bg.jpg', // Custom background image
width: double.infinity,
height: double.infinity,
// opacity: const AlwaysStoppedAnimation(.85),
fit: BoxFit.cover,
),
Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Align(
alignment: Alignment.bottomCenter,
child: Text(
'Welcome to',
style: TextStyle(
fontFamily: "AldotheApache",
color: Colors.white,
fontSize: 24.0,
fontWeight: FontWeight.bold,
shadows: [
Shadow(
blurRadius: 5.0,
color: Colors.black.withOpacity(0.5),
offset: Offset(2.0, 2.0),
),
],
),
),
),
const SizedBox(height: 10.0),
Text(
'The Theatre of Quiz',
style: TextStyle(
fontFamily: "AldotheApache",
color: ColorsToUse().unitedRed,
fontSize: 36.0,
fontWeight: FontWeight.bold,
letterSpacing: 2.0,
shadows: [
Shadow(
blurRadius: 5.0,
color: Colors.black.withOpacity(0.5),
offset: Offset(2.0, 2.0),
),
],
),
textAlign: TextAlign.center,
),
const SizedBox(height: 30.0),
TextField(
controller: _emailController,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white.withOpacity(0.9),
hintText: 'Enter your email',
prefixIcon: const Icon(Icons.email),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
const SizedBox(height: 20.0),
TextField(
controller: _passwordController,
obscureText: obscure,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white.withOpacity(0.9),
hintText: 'Enter your password',
prefixIcon: const Icon(Icons.lock),
suffixIcon: IconButton(
onPressed: () {
setState(() {
obscure = !obscure;
});
},
icon: const Icon(
Icons.remove_red_eye,
size: 20,
)),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
),
const SizedBox(height: 20.0),
ElevatedButton(
onPressed: () {
_login(context);
},
style: ElevatedButton.styleFrom(
backgroundColor:
Colors.white,
padding: const EdgeInsets.symmetric(vertical: 15.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
shadowColor: Colors.black,
elevation: 5,
),
child: const Text(
'Login',
style: TextStyle(fontSize: 18.0),
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => RegisterScreen()));
},
style: ElevatedButton.styleFrom(
backgroundColor:
Colors.white, // Manchester United's primary color
padding: const EdgeInsets.symmetric(vertical: 15.0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
shadowColor: Colors.black,
elevation: 5,
),
child: const Text(
'Register',
style: TextStyle(fontSize: 18.0),
),
),
],
),
),
),
],
),
);
}
}
This line String? token = await authService.login(username, password);
return null for some reason.
Can anyone help?
I was expecting the token to returned after calling the api in the authService file.
2
Answers
Apparently the error was due to the port not being free for the express API.I changed the port number and it is working fine now.
It’s hard to debug without a real URL, but it can help to use Postman’s code generation feature
In Postman, you can use the "Code" feature to generate the Dart code for the API request. This can help you compare the generated code with your Flutter code to identify any issues.
Also, you can directly use the email/password values directly, hard coded just for debugging purposes, and see if that helps.