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);
}
});
}
Related
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;
}
Referring to this article
https://medium.com/solidmvp-africa/making-your-api-calls-in-flutter-the-right-way-f0a03e35b4b1
I was trying to call API from a flutter app. But to make it the right way, I was looking for a complete example and came here. My question is why do I need to create an ApiBaseHelper class then RepositoryClass then all other formalities to call an API. Why can't I use FutureBuilder and a simple async function associated with the API like this:
class Networking {
static const BASE_URL = 'https://example.com';
static Future<dynamic> getProductById({
required String? token,
required String? productId,
}) async {
final url = Uri.parse('$BASE_URL/products/$productId');
final accessToken = 'Bearer $token';
Map<String, String> requestHeaders = {
'Authorization': accessToken,
'Content-Type': 'application/json'
};
try {
final response = await http.get(
url,
headers: requestHeaders,
);
if (response.statusCode != 200) {
throw Exception('Error fetching data.');
}
final responseJSON = json.decode(response.body);
if (responseJSON['error'] != null) {
return throw Exception(responseJSON['error']);
}
final product = Product.fromJson(responseJSON);
return product;
} catch (e) {
throw Exception(e.toString());
}
}
}
And then calling it from a FutureBuilder like this:
FutureBuilder(
future: Networking.getProductById(token, id),
builder: (context, snapshot) {
// rest of the code
}
)
Can anyone tell me what is the most convenient and widely used way to call an API?
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.
I'm trying to display data from the API to the screen, but I can't do it because of the 401 error, take a look at my code and tell me what exactly is wrong, I think that I wrote the API incorrectly. At the moment I am trying to find the information myself, but I think the problem is in the API, and if so, what exactly is the problem?
Code :
API:
class ApiService {
Dio dio = new Dio();
var token ="token";
var refresh_token ="token";
Future getUserCards() async {
try {
Response resp;
var get_cards = "https://example/api/cards";
resp = await dio.get(get_cards);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
var json = (resp.data);
var value = json["id"]["row"]["seq_num"]["text"];
return value;
} catch (e) {
print(e);
}
Future loginUser(String username, String password) async {
var storage = new FlutterSecureStorage();
await storage.write(key: 'JWT', value: token);
var login = "https://example/users/login/";
final data = {"username": username, "password": password};
Response response;
response = await dio.post(login, data: data);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
if (response.statusCode == 200) {
Get.to(CardScreen());
return response.data;
} else if (response.statusCode == 401) {
var refreshToken = await dio.post(
"https://example.api/cards/refresh/");
response = await dio.post(refresh_token, data: data);
dio.options.headers["Authorization"] =
"Bearer ${token},'Content-Type': 'application/json','refresh_token': '$refresh_token'";
storage = response.data["token"];
refresh_token = response.data["refresh_token"];
return loginUser("username", "password");
} else
return null;
}
}
UI :
children: [
Expanded(
child: FutureBuilder<dynamic>(
future: ApiService().getUserCards(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Card(
child: Text(snapshot.data[index]),
);
});
}
},
)
I need to use some data from a JSON response body in an if statement. How would I go about accessing this data from the file that calls the function that performs the POST? (Or other files) Any help is appreciated! Thanks!
This is the function that performs the POST.
Future<User> loginUser(String username, String password) async {
final http.Response response = await http.post(
'https://fakeapiofcourse.com/login',
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'username': username,
'password': password
}),
);
if (response.statusCode <400) {
return User.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to login user');
}
}
This is where I'll need to use the data first. In the If statement, I'll be using a Bool from the response body.
onPressed: () async {
bool success = true;
try {
await loginUser(passwordController.text, nameController.text);
} on Exception {
success = false;
}
// print(nameController.text);
-> if(THIS IS WHERE I NEED TO CHECK THE BOOL FROM THE BODY) {
Navigator.push(
context,
PageRouteBuilder(
transitionDuration: Duration(seconds: 1),
transitionsBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secAnimation,
Widget child) {
animation = CurvedAnimation(
parent: animation,
curve: Curves.elasticInOut);
return ScaleTransition(
scale: animation,
child: child,
alignment: Alignment.center,
);
},
pageBuilder: (BuildContext context,
Animation<double> animation,
Animation<double> secAnimation) {
return Dashboard();
}));}
},
Try something like this.
dynamic loginUser(String username, String password) async {
final http.Response response = await http.post(
'https://fakeapiofcourse.com/login',
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, String>{
'username': username,
'password': password
}),
);
if (response.statusCode < 400) {
return json.decode(response.body);
} else {
throw Exception('Failed to login user');
}
}
Get the response from the function, then use it.
onPressed: () {
bool success = true;
var response;
try {
response = loginUser(passwordController.text, nameController.text);
User currentUser = User.fromJson(response);
} on Exception {
success = false;
}
if(response['SOME_FIELD'] == 'SOME_VALUE'){
.....
}else{
....
}
}
If you don't want this, simply just add that field to your User model, so that you can access it.
Hope that suits your case!
You can get your user object in this way:
var User user = null
try{
user = await loginUser(passwordController.text, nameController.text);
}on Exception{
success = false;
}
if(user.loggedIn && user != null){
Navigator.push()
}
Wait, you are calling loginUser but are not assigning the result to anything.
So just change your code to do:
onPressed: () async {
User user;
try {
user = await loginUser(passwordController.text, nameController.text);
} on Exception {
if (user != null) {
// now you can access whatever field of user you want
}
else {
// handle exception or a null user from your loginUser callcases where the
}