how to login by API using Provider State management in Flutter - flutter

what is the correct way to do login/authentication using Rest API in flutter using provider,
every time i get the below error in my console,
I/flutter (18602): 500
I/flutter (18602): {"message":"data and hash arguments required"}
this is my provider page,:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class LoginController extends ChangeNotifier{
bool _isLoading = false;
bool get isLoading => _isLoading;
void loginDemo({
required String email,
required dynamic password,
}) async {
_isLoading = true;
notifyListeners();
String urls = 'https://elated-pink-hedgehog.cyclic.app/login';
final response = await http.post(Uri.parse(urls),
body: ({
"email":email,
"password":password
}));
if (response.statusCode == 200) {
print(response.body);
print('login successfull');
notifyListeners();
}else{
print(response.statusCode);
print(response.body);
notifyListeners();
}
notifyListeners();
}
}
this is where i call the login Function in UI:
Consumer<LoginController>(
builder: (context, value, child) =>
ElevatedButton(
onPressed:(){
if (emailController.text.isEmpty || passwordController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content:Text('All Field Required')));
}else{
value.loginDemo(
email: emailController.text,
password: passwordController.text
);
}
},
child:Text('login')),
),
please help me to solve this. iam new to flutter and provider.

I think the error comes from the way you post your body do this instead
void loginDemo({
required String email,
required dynamic password,
}) async {
_isLoading = true;
notifyListeners();
final Map<String, dynamic> loginData = {
'email': email,
'password': password
};
Response response = await post(
Uri.parse('https://elated-pink-hedgehog.cyclic.app/login'),
body: json.encode(loginData),
headers: {'Content-Type': 'application/json'},
);
if (response.statusCode == 200) {
final Map<String, dynamic> responseData = json.decode(response.body);
//if you have a model class then inject it here
UserModel authUser = UserModel.fromJson(responseData);
_isLoading = false;
notifyListeners();
}else{
print(response.statusCode);
print(response.body);
notifyListeners();
}
notifyListeners();
}

Related

I need your help for an error I encounter in flutter dart

I have an application and I created a legin and logout page... and when I click on my application's logout button, I get an error like this " Null check operator used on a null value"*and when I point to the error, it tells me [1] :
https://i.stack.imgur.com/n0uJ8.pngentrez
import 'dart:async';
import 'dart:convert';
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:logger/logger.dart';
import '../db/db_auth_shared_preference.dart';
import '../network/app_urls.dart';
import '../models/auth.dart';
enum Status {
notLoggedIn,
loggedIn,
authenticating,
loggedOut,
notReet,
reseted,
resetting
}
//Help display the logs
var logger = Logger();
class AuthProvider with ChangeNotifier {
Auth? _auth;
Auth get auth => _auth!;
void setAuth(Auth auth) {
_auth = auth;
notifyListeners();
}
bool isAuth() {
if (_auth == null || auth.token == '') {
return false;
}
return true;
}
// Time before the token expires
Timer? _authTimer;
DateTime? _expiryDate;
String? username;
String? password;
// Set the status of the user to Not Logged In at the start of the app
Status _status = Status.notLoggedIn;
Status get status => _status;
// Change the status of the user
set status(Status value) {
_status = value;
notifyListeners();
}
// Log In the user
Future<Map<String, dynamic>> login(String email, String password) async {
Map<String, Object> results;
final Map<String, dynamic> loginData = {
'email': email,
'password': password
};
status = Status.authenticating;
logger.d("--- Authentication ---");
try {
Response response = await post(
Uri.parse(
"${AppUrl.login}? username=${loginData['email']}&password=${loginData['password']}"
),
);
logger.d('Login response : ${response.statusCode}');
// The Request Succeded
if (response.statusCode == 200) {
final Map<String, dynamic> responseData =
json.decode(utf8.decode(response.bodyBytes));
var requestStatus = responseData["status"];
if (requestStatus != 0) {
status = Status.notLoggedIn;
results = {'status': false, 'message': "La Connexion a échoué"};
} else {
// Get the status code of the request
Map<String, dynamic> authData = responseData["utilisateurJson"];
logger.d(authData);
_expiryDate = DateTime.now().add(const Duration(seconds: 3500));
//store user shared pref
Auth authUser = Auth.fromMap(authData,
timeToExpire: _expiryDate,
username: loginData['email'],
password: loginData['password']);
_expiryDate = authUser.expiryDate;
logger.wtf(_expiryDate);
//clear session data
AuthPreferences().removeAuth();
//store User session
AuthPreferences().saveAuth(authUser);
setAuth(authUser);
status = Status.loggedIn;
username = loginData["email"];
password = loginData["password"];
results = {
'status': true,
'message': 'Successful login',
'auth': authUser,
};
autoLogOut();
}
} else {
status = Status.notLoggedIn;
results = {'status': false, 'message': 'La Connexion a échoué'};
}
return results;
} catch (e) {
logger.e(e);
status = Status.notLoggedIn;
results = {
'status': false,
'message': "La Connexion avec le serveur a échoué"
};
return results;
} }
void autoLogOut() {
if (_authTimer != null) {
_authTimer!.cancel();
}
final timeToExpiry = _expiryDate!.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timeToExpiry),
() async => await login(username!, password!));
}
// Log Out the User
void logOut() {
logger.d("--- User Logging Out ---");
AuthPreferences().removeAuth();
status = Status.loggedOut;
_expiryDate = null;
_auth = null;
logger.d("--- User Logged Out ---");
}
Future<Auth?> tryAutoLogin() async {
final authSession = await AuthPreferences().getAuth();
if (authSession == null) {
return null;
}
logger.d("The expiry time is : ${authSession.expiryDate}");
if (authSession.expiryDate.isBefore(DateTime.now())) {
login(authSession.username, authSession.password);
return authSession;
}
_expiryDate = authSession.expiryDate;
setAuth(authSession);
logger.d("SETTING THE USER");
autoLogOut();
return authSession;
}
}
Error Explanation: Bang operator(!) means that in flutter, when you use this operator, you are completely sure that variable is not going to be null in any case.
There are two ways to resolve it -
Use if conditional to confirm that variable is not null
Use null-aware or if-null operator ?? like
Auth get auth => _auth ?? Auth();
Since you didn't provide any error logs; based on attached image and as your cursor on line no 29, _auth variable is null. So before using ! make sure your variable is not null.

How to dynamically save token after logging to shared prefernces

How to dynamically auth users and save tokens in shared pref?
I understood how to save token in sharedprefernces, but can't understand how to take it dynamically by login/password and pass token from it to sharedpref dynamically in loginWithToken(); beacuse I use this function for auth in
final httpConnectionOptions = HttpConnectionOptions(
accessTokenFactory: () => SharedPreferenceService().loginWithToken(),
and it is required only String
My code now is like that:
Here is request where I am making request to get token:
Future<String?> getToken(String password, String login) async {
String _email = "admin";
String _password = "123";
Map<String, String> headers = {
'Content-Type': 'application/json',
'accept': ' */*'
};
final body = {
'username': _email,
'password': _password,
};
var response = await http.post(
Uri.parse("http://mylink/login"),
headers: headers,
body: jsonEncode(body),
);
if (response.statusCode == 200) {
var value = jsonEncode(response.body);
return value;
}
return null;
}
here is I created logging logic:
final TextEditingController _loginController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
ElevatedButton(
onPressed: () async {
var username = _loginController.text;
var password = _passwordController.text;
var jwt = await ProviderService()
.getToken(password, username);
if (jwt != null) {
SharedPreferenceService().setToken(jwt);
Navigator.pushNamed(
context, '/mainPageAdmin');
} else {
displayDialog(context);
}
},
here is my shared pref. I can't understand how to put new token value in that string, after paaword and login was sent.
String tokens = 'dhjwhdwdwkjdhdkje';
Future<bool> getSharedPreferencesInstance() async {
_prefs = await SharedPreferences.getInstance().catchError((e) {
print("shared preferences error : $e");
return false;
});
return true;
}
Future setToken(String token) async {
await _prefs?.setString('token', token);
}
Future clearToken() async {
await _prefs?.clear();
}
Future<String> get token async => _prefs?.getString('token') ?? '';
Future<String> loginWithToken() async {
bool value = await getSharedPreferencesInstance();
if (value == true) {
setToken("Bearer $tokens");
// print(tokens);
}
return tokens;
}
Api Responce:
{
"$id": "1",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZ",
"user": {
"$id": "2"
}
}
Auth class I parsed:
Auth authFromJson(String str) => Auth.fromJson(json.decode(str));
String authToJson(Auth data) => json.encode(data.toJson());
class Auth {
Auth({
this.token,
this.user,
});
final String? token;
final User? user;
factory Auth.fromJson(Map<String, dynamic> json) => Auth(
token: json["token"],
user: User.fromJson(json["user"]),
);
Map<String, dynamic> toJson() => {
"token": token,
"user": user!.toJson(),
};
}
In your getToken function do this:
if (response.statusCode == 200) {
var value = jsonEncode(response.body) as Map<String, dynamic>;
await setToken(value['token']);
return value;
}

how to resend multipart request

I am retrying my api call if get 401 response but when Retrying I am ending with an following exception
following is my code for retrying multipart I had used http_interceptor package for retrying Api Calls
interceptor.dart
class AuthorizationInterceptor extends InterceptorContract {
#override
Future<BaseRequest> interceptRequest({required BaseRequest request}) async {
final prefs = await SharedPreferences.getInstance();
final extractData =
json.decode(prefs.getString('userData')!) as Map<String, dynamic>;
final Map<String, String> headers = Map.from(request.headers);
headers['Authorization'] = await extractData['accessToken'];
print(
'this is from AuthorizationInterceptor: ${extractData['accessToken']}');
// TODO: implement interceptRequest
return request.copyWith(
headers: headers,
);
}
retry.dart
class ExpiredTokenRetryPolicy extends RetryPolicy {
BuildContext context;
ExpiredTokenRetryPolicy(this.context);
#override
// TODO: implement maxRetryAttempts
int get maxRetryAttempts => 2;
#override
Future<bool> shouldAttemptRetryOnResponse(BaseResponse response) async {
if (response.statusCode == 401) {
print('retry token started');
//perform token refresh,get the new token and update it in the secure storage
await Provider.of<Auth>(context, listen: false).restoreAccessToken();
return true;
}
return false;
}
I am using interceptors in my widget following is my code where I am using interceptors and using retry policy
#override
Widget build(BuildContext context) {
var flutterFunctions = Provider.of<FlutterFunctions>(context);
// print('this is from insert package${token.token}');
ApiCalls repository = ApiCalls(
client: InterceptedClient.build(
retryPolicy: ExpiredTokenRetryPolicy(context),
interceptors: [
AuthorizationInterceptor(),
],
),
);
following is my restore access token method
Future<void> restoreAccessToken() async {
print('restoreAccessToken started');
//print(token);
final url = '${Ninecabsapi().urlHost}${Ninecabsapi().login}/$sessionId';
var response = await http.patch(
Uri.parse(url),
headers: {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': accessToken!
},
body: json.encode(
{"refresh_token": refreshtoken},
),
);
var userDetails = json.decode(response.body);
if (response.statusCode == 401) {
print(userDetails['messages']);
}
sessionId = userDetails['data']['session_id'];
accessToken = userDetails['data']['access_token'];
accessTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['access_token_expiry']),
);
refreshToken = userDetails['data']['refresh_token'];
refreshTokenExpiryDate = DateTime.now().add(
Duration(seconds: userDetails['data']['refresh_token_expiry']),
);
final userData = json.encode({
'sessionId': sessionId,
'refreshToken': refreshToken,
'refreshExpiry': refreshTokenExpiryDate!.toIso8601String(),
'accessToken': accessToken,
'accessTokenExpiry': accessTokenExpiryDate!.toIso8601String()
});
//print(userDetails);
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.setString('userData', userData);
print("this is from restoreAcessToken :$userDetails");
final extractData =
json.decode(prefs.getString('userData')!) as Map<String, dynamic>;
print('restore access token: ${extractData['accessToken']}');
reset();
}
As a rule. You must NOT write using the same Stream/MultipartFile more than once. If you need to retry sending to the same destination, you have to use a new MultipartFile each time you retry.

Get token auth value to another dart using sharedprefence

how to retrieve token variable from sharedprefence in flutter?
i am very new to implement api for my flutter project because previously I was only told to work on the frontend, i have saved auth token in login and here is my code to store token in sharedprefence
signIn(String email, password) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email': email,
'password': password
};
var jsonResponse = null;
var response = await http.post(Uri.parse("/api/login"), body: data);
if(response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if(jsonResponse != null) {
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['data']['token']['original']['token']);
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => HomePage()), (Route<dynamic> route) => false);
}
}
else {
setState(() {
_isLoading = false;
});
scaffoldMessenger.showSnackBar(SnackBar(content:Text("Mohon cek Email dan Password kembali!", textAlign: TextAlign.center,), backgroundColor: Colors.red,));
}
}
and here is the darts place that I want to call the token for auth in the post method
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:kiriapp/models/angkot.dart';
class AngkotProvider with ChangeNotifier {
late AngkotModel _angkot;
AngkotModel get angkot => _angkot;
set angkot(AngkotModel newAngkot) {
_angkot = newAngkot;
notifyListeners();
}
static Future<AngkotModel?> tambah(
String user_id,
String route_id,
String plat_nomor,
String pajak_tahunan,
String pajak_stnk,
String kir_bulanan) async {
try {
var body = {
'user_id': user_id,
'route_id': route_id,
'plat_nomor': plat_nomor,
'pajak_tahunan': pajak_tahunan,
'pajak_stnk': pajak_stnk,
'kir_bulanan': kir_bulanan,
};
print(body);
var response = await http.post(
Uri.parse('api/create'),
headers: {
'Authorization': 'Bearer $howtocallthetoken?,
},
body: body,
);
print(response.statusCode);
print(response.body);
if (response.statusCode == 201) {
return AngkotModel.fromJson(jsonDecode(response.body));
} else if (response.statusCode == 400) {
return AngkotModel.fromJson(jsonDecode(response.body));
}{
return null;
}
} catch (e) {
print(e);
return null;
}
}
}
thanks
To store something in shared preference we use setString function, just like you did. Now to retrieve it, you should use getString and it will return the token you stored earlier.
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String accessToken = sharedPreferences.getString("token");
var response = await http.post(
Uri.parse('api/create'),
headers: {
'Authorization': 'Bearer $accessToken',
},
body: body,
);
Don't forget to make the function async, and handle null values as the getString function might return token as null if not stored correctly.

How to use response after POST Request?

How to use data from response after POST request in Flutter/Dart?
this is my function:
signIn(String email, pass) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email': email,
'password': pass
};
var jsonResponse = null;
var response = await http.post("http://10.0.2.2:80/user/login", body: data);
if(response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if(jsonResponse != null) {
print(jsonResponse);
setState(() {
_isLoading = true;
});
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => MainPage()), (Route<dynamic> route) => false);
}
}
else {
setState(() {
_isLoading = false;
});
print(response.body);
}
}
after run this function, my jsonResponse returns this data to me:
{id: 1, firstName: admin, lastName: admin, accountName: Kot filemon, email: admin#admin.pl, active: false, activateCode: 0, admin: true, latitude: xxx, longitude: xxx, profileImage: null}
so, how can I use this data(without another request to server) in my app on different screen?
I know how to use this type of data in react, because i'm a frontend developer but i don't have any idea how to use this here.
thanks for any help :)
You can save it to SharedPreferences if all the data in this response are common for all screens.
At this moment you cant store all that jsondata in one SharedPreferenceValue. You need to create one value foreach userdata like this:
signIn(String email, pass) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map data = {
'email': email,
'password': pass
};
var jsonResponse = null;
var response = await http.post("http://10.0.2.2:80/user/login", body: data);
if(response.statusCode == 200) {
jsonResponse = json.decode(response.body);
if(jsonResponse != null) {
print(jsonResponse);
// this lines save the user data in the sharedpreferenceinstance
await prefs.setString('email',jsonResponse[email]);
await prefs.setString('accountName', jsonResponse[accountName]);
//-----------------now you cant send to another page
// if you need to read the saved data use=>prefs.getInt('email');
setState(() {
_isLoading = true;
});
Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (BuildContext context) => MainPage()), (Route<dynamic> route) => false);
}
}
else {
setState(() {
_isLoading = false;
});
print(response.body);
}
}