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.
Related
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();
}
hello I have a case where when the user token expires the user does not switch to the loginPage page, even though I have set it here.
how do i solve this problem thanks.
i set it on splashscreen if token is not null then go to main page and if token is null then go to login page.
but when the token expires it still remains on the main page
Future<void> toLogin() async {
Timer(
const Duration(seconds: 3),
() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString(Constant.token);
Navigator.pushReplacementNamed(
context,
token != null ? AppRoute.mainRoute : AppRoute.loginRoute,
arguments: token,
);
},
);
}
and function when user login
CustomButtonFilled(
title: 'Login',
onPressed: () async {
final prefs =
await SharedPreferences.getInstance();
prefs.setString(Constant.token, '');
if (nimController.text.isEmpty ||
passwordController.text.isEmpty) {
showError('NIM/Password harus diisi');
} else {
setState(() {
isLoading = true;
});
User? user = await userProvider.login(
nimController.text,
passwordController.text);
setState(() {
isLoading = false;
});
if (user == null) {
showError('NIM/Password tidak sesuai!');
} else {
userProvider.user = user;
Navigator.pushNamedAndRemoveUntil(
context,
'/main',
(route) => false,
);
}
}
},
),
and this call api
Future<User?> login(String nim, String password) async {
String url = Constant.baseURL;
try {
var body = {
'username': nim,
'password': password,
};
var response = await http.post(
Uri.parse(
'$url/login_mhs',
),
body: body,
);
if (response.statusCode == 200) {
final token = jsonDecode(response.body)['data']['access_token'];
//Ini mulai nyimpen token
await UtilSharedPreferences.setToken(token);
print(token);
// print(await UtilSharedPreferences.getToken());
return User.fromJson(jsonDecode(response.body));
} else {
return null;
}
} catch (e) {
print(e);
throw Exception();
}
}
you can just make your own HTTP client using Dio and add Interceptor to automatically regenerate idToken if expired using the refreshToken given.
Http client gives an error if the refreshToken also gets expired.
In that case, just navigate to the login screen.
Full code for adding interceptor and making own HTTP client is given below
import 'package:dio/dio.dart';
import '../utils/shared_preference.dart';
class Api {
static Dio? _client;
static Dio clientInstance() {
if (_client == null) {
_client = Dio();
_client!.interceptors
.add(InterceptorsWrapper(onRequest: (options, handler) async {
if (!options.path.contains('http')) {
options.path = 'your-server' + options.path;
}
options.headers['Authorization'] =
'Bearer ${PreferenceUtils.getString('IdToken')}';
return handler.next(options);
}, onError: (DioError error, handler) async {
if ((error.response?.statusCode == 401 &&
error.response?.data['message'] == "Invalid JWT")) {
if (PreferenceUtils.exists('refreshToken')) {
await _refreshToken();
return handler.resolve(await _retry(error.requestOptions));
}
}
return handler.next(error);
}));
}
return _client!;
}
static Future<void> _refreshToken() async {
final refreshToken = PreferenceUtils.getString('refreshToken');
final response = await _client!
.post('/auth/refresh', data: {'refreshToken': refreshToken});
if (response.statusCode == 201) {
// successfully got the new access token
PreferenceUtils.setString('accessToken', response.data);
} else {
// refresh token is wrong so log out user.
PreferenceUtils.deleteAll();
}
}
static Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
final options = Options(
method: requestOptions.method,
headers: requestOptions.headers,
);
return _client!.request<dynamic>(requestOptions.path,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters,
options: options);
}
}
Dio client = Api.clientInstance();
var resposne = (hit any request);
if(error in response is 401){
//it is sure that 401 is because of expired refresh token as we
//already handled idTokoen expiry case in 401 error while
//adding interceptor.
navigate to login screen for logging in again.
}
Please accept the solution if it solves your problem.
If your session expire feature has some predefine interval or logic than you have to implement it in splash screen and based on that you can navigate user further. Otherwise you want to handle it in API response only you have add condition for statusCode 401.
checkSessionExpire(BuildContext context)
if (response.statusCode == 200) {
//SuccessWork
} else if (response.statusCode == 401) {
//SessionExpire
} else {
return null
}
}
how to pass the token value from above code to another screen into a http-request as variable.
class AppStarted extends AuthenticationEvent {}
class LoggedIn extends AuthenticationEvent {
final String token;
const LoggedIn({#required this.token});
#override
List<Object> get props => [token];
#override
String toString() => 'LoggedIn { token: $token }';
}
Thank you for your help, haning on this for 2 hours now...
first create a class where your where https request handle & token store
class Network {
var token;
final string base= "/api";
//token get when needed
_getToken() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var user = jsonDecode(localStorage.getString('data'));
token = user['token'];
}
//signup
signUp(data, apiUrl) async {
var fullUrl = baseUrl + apiUrl;
return await http.post(fullUrl,
body: jsonEncode(data), headers: _setHeaders());
}
//login
signIn(apiUrl) async {
var fullUrl = baseUrl + apiUrl;
await _getToken();
return await http.get(fullUrl, headers: _setHeaders());
}
}
in sign up page use this method where you use sign up button on pressed
void _signUp() async {
var data = {
'mobile': mobileController.text,
'password': mobileController.text,
};
var res = await Network().signUp(data, '/register');
var body = json.decode(res.body);
if (body.statusCode == 200) {
SharedPreferences localStorage = await SharedPreferences.getInstance();
localStorage.setString('token', json.encode(body['data']['token']));
localStorage.setString('data', json.encode(body['data']));
Navigator.push(
context,
new MaterialPageRoute(builder: (context) => Home()),
);
} else {
// errorMessage = "as you want to show ";
}
}
in sign up page , use this method same as sign up pages
void signIn() async {
var data = {'mobile': mobile, 'password': password};
var res = await Network().signIn(data, '/login');
var body = json.decode(res.body);
if (res.statusCode == 200) {
SharedPreferences localStorage = await SharedPreferences.getInstance();
localStorage.setString('token', json.encode(body['data']['token']));
localStorage.setString('data', json.encode(body['data']));
Navigator.pushReplacement(
context,
new MaterialPageRoute(builder: (context) => HomePage()),
);
} else if (res.statusCode != 200) {
// mobileError = "These credentials do not match our records.";
}
}
now you can have the token when u signup/singIn and store token in sharedSharedPreferences
whenever you try to acces token just called _getToken() method
I have to check the email and password with the rest API that is going well. The problem is my future is returning a class object that has a token. I need that that for other screen and after login navigate to other screens.
Future<LoginResponse> createLoginState(String email, String password) async {
final http.Response response = await http.post(
'https://www.polestarkw.com/api/login',
headers: <String, String>{
'Accept': 'application/json',
//'content-type' : 'application/json'
},
body: {
"email":email ,
"password":password ,
});
if (response.statusCode == 200) {
// print(response.body);
LoginResponse loginResponse=LoginResponse.fromJson(json.decode(response.body)) ;
return loginResponse;
} else {
throw Exception('Failed to create album.');
}
}
class LoginResponse {
Object _data;
String token_type;
String expires_in;
String access_token;
String refresh_token;
LoginResponse(
{this.token_type, this.expires_in, this.access_token, this.refresh_token});
LoginResponse.fromJson(Map<String, dynamic> json) {
token_type = json['token_type'];
expires_in = json['expires_in'];
access_token = json['access_token'];
refresh_token = json['refresh_token'];
}
}
I need this loginResponse object on my other page. Here is using a future instance.
_futureJwt = createLoginState(emailController.text, pwdController.text);
how to get data from _futureJwt.
The code should go something like this
Future<LoginResponse> createLoginState(String email, String password) async {
final http.Response response = await http.post(
'https://www.polestarkw.com/api/login',
headers: <String, String>{
'Accept': 'application/json',
//'content-type' : 'application/json'
},
body: {
"email":email ,
"password":password ,
});
if (response.statusCode == 200) {
// print(response.body);
LoginResponse loginResponse=fromJson(json.decode(response.body)) ;
return loginResponse;
} else {
throw Exception('Failed to create album.');
}
}
LoginResponse fromJson(Map<String, dynamic> json) {
token_type = json['token_type'];
expires_in = json['expires_in'];
access_token = json['access_token'];
refresh_token = json['refresh_token'];
return LoginResponse(token_type,expires_in,access_token,refresh_token);
}
class LoginResponse {
Object _data;
String token_type;
String expires_in;
String access_token;
String refresh_token;
LoginResponse(
{this.token_type, this.expires_in, this.access_token, this.refresh_token});
}
The above code should work in the way u have written it too but I am not sure since I use this way
Then you can use this like
LoginResponse _futureJwt = await createLoginState(emailController.text, pwdController.text);
var token_type = _futureJwt.token_type;
var expires_in = _futureJwt.expires_in;
var access_token = _futureJwt.access_token;
var refresh_token = _futureJwt.refresh_token;
As simple as that. If you do not want to wait for the Future, you can use .then like this
createLoginState(emailController.text, pwdController.text).then((_futureJwt){
var token_type = _futureJwt.token_type;
var expires_in = _futureJwt.expires_in;
var access_token = _futureJwt.access_token;
var refresh_token = _futureJwt.refresh_token;
});
Use FutureBuilder.
Then you can use AsyncSnapshot to access hasData(), hasError() and get the data like so:
#override
Widget build(BuildContext context) {
Future<String> exampleFuture = Future.delayed(Duration(seconds: 2), "value")
return FutureBuilder(
future: exampleFuture,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasError) {
return Text("error");
} else if (!snapshot.hasData) {
return Text("loading");
} else {
return Text(snapshot.data);
}
});
}
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);
}
}