Passing Variable Token Key value in a Flutter Project - flutter

I have an api_service.dart file and when the user logs in I save the json.decode(response.body)['key'] in a variable as Tkey. I want to be able to access it in the same file but in a different function when I am trying to access user details:
I am not sure how to apply this answer How to pass access token value in flutter to my code
class APIService {
static var client = http.Client();
static Future<bool> login(
LoginRequestModel model,
) async {
Map<String, String> requestHeaders = {
'Content-Type': 'application/json', };
var url = Uri.parse(
Config.apiURL + Config.loginAPI, );
var response = await client.post(
url,
headers: requestHeaders,
body: jsonEncode(model.toJson()), );
if (response.statusCode == 200) {
await SharedService.setLoginDetails(
loginResponseJson(
response.body, ), );
print("No.2 Test ${response.body}"); <-------{"key":"xxxxxxxxxx"}
var Tkey = json.decode(response.body)['key'];
print("No.2 Test $Tkey"); <-------------- xxxxxxxxxxxxx
return true;
} else {
return false; } }
static Future<User> fetchUser() async {
var url = Uri.parse(Config.apiURL + Config.userProfileAPI);
final response = await http.get(
url,
headers: {
HttpHeaders.authorizationHeader:
'Token $Tkey', <--------------- I want to print here the value of the key
}, );
final responseJson = jsonDecode(response.body);
print(responseJson);
if (response.statusCode == 200) {
return User.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load User');
} }}
My question how can I get access to the key to be used in the fetchUser() ?

Declare a global variable in the global scope or in a separate file, then when this method is executing, assign that key to it, then use it everywhere else in your app.
in a separate file:
String GlobalTkey = "";
now in your method replace the following:
print("No.2 Test ${response.body}"); <-------{"key":"xxxxxxxxxx"}
var Tkey = json.decode(response.body)['key'];
print("No.2 Test $Tkey"); <-------------- xxxxxxxxxxxxx
with this:
print("No.2 Test ${response.body}"); <-------{"key":"xxxxxxxxxx"}
var Tkey = json.decode(response.body)['key'];
GlobalTkey = Tkey; // you need to import the file where the variable exists
print("No.2 Test $Tkey"); <-------------- xxxxxxxxxxxxx
now everywhere in your app including different files, methods, widgets, classes... you can use GlobalTkey

Related

Flutter upload image

Get Api Flutter dont working
I tried various methods, if the link is wrong, then it should at least be displayed json text in terminal
photo should be shown
Future<dynamic> getPhotoUrl(int profileID) async {
print("get Photo url $profileID");
var client = http.Client();
var url = Uri.parse("$profileBaseUrl/api/v2/profiles/$profileID/photos");
Map<String, String> headers = {
'APIVersion': '1',
"Authorization": token,
};
var response = await client.get(url, headers: headers);
if (200 == response.statusCode) {
return response.body;
} else {
}
print("avatar url: $currentPhotoUrl");
}
tried this and it doesn't work
Future<void> getPhotoUrl(int profileID) async {
print("get photo url $profileID");
var client = http.Client();
Map<String, String> headers = {
"Authorization": token
};
final http.Response response = await client.get(
Uri.parse("$profileBaseUrl/api/v2/profiles/$profileID/photos"),
headers: headers);
if (response.statusCode == 200) {
Map responseBody = jsonDecode(response.body);
var data = responseBody["data"];
if (data.length < 1) {}
else {
currentPhotoUrl.value = data[0]["content"][0]["medium"];
}
} else {
throw WebSocketException("server error: ${response.statusCode}");
}
print("photos url: $currentPhotoUrl");
}

flutter dio(4.0.0) handling token expiration (handling 401)

I have declared a class to make api requests using flutter Dio as follows.
class DioUtil {
static Dio _instance;
static Dio getInstance() {
if (_instance == null) {
_instance = createDio();
}
return _instance;
}
static Dio createDio() {
var dio = Dio();
dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
// Do something before request is sent
return handler.next(options); //continue
}, onResponse: (response, handler) {
// Do something with response data
return handler.next(response); // continue
}, onError: (DioError e, handler) async {
if (e.response != null) {
if (e.response.statusCode == 401) {
var dio = DioUtil.getInstance();
dio.interceptors.requestLock.lock();
dio.interceptors.responseLock.lock();
RequestOptions requestOptions = e.requestOptions;
await refreshToken();
Repository repository = Repository();
var accessToken = await repository.readData("accessToken");
final opts = new Options(
method: requestOptions.method
);
dio.options.headers["Authorization"] = "Bearer " + accessToken;
dio.interceptors.requestLock.unlock();
dio.interceptors.responseLock.unlock();
dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
}//TODO: handle else clause
}
}));
return dio;
}
static refreshToken() async {
Response response;
Repository repository = Repository();
var dio = Dio();
final Uri apiUrl = Uri.parse(BASE_PATH + "auth/reIssueAccessToken");
var refreshToken = await repository.readData("refreshToken");
dio.options.headers["Authorization"] = "Bearer " + refreshToken;
response = await dio.postUri(apiUrl);
if (response.statusCode == 200) {
LoginResponse loginResponse =
LoginResponse.fromJson(jsonDecode(response.toString()));
repository.addValue('accessToken', loginResponse.data.accessToken);
repository.addValue('refreshToken', loginResponse.data.refreshToken);
} else {
print(response.toString());
}
}
}
and I use flutter bloc pattern and my bloc is as follows.
class OurClassBloc extends Bloc<OurClassEvent, OurClassState> {
OurClassBloc(OurClassState initialState) : super(initialState);
Repository repository = Repository();
#override
Stream<OurClassState> mapEventToState(
OurClassEvent event,
) async* {
if (event is GetClasses) {
yield* _getClassCategories(event);
}
}
Stream<OurClassState> _getClassCategories(GetClasses event) async* {
Response response;
var dio = DioUtil.getInstance();
final String apiUrl = (BASE_PATH + "classCategories");
var accessToken = await repository.readData("accessToken");
Map<String, dynamic> map = {"active": event.active};
dio.options.headers["Authorization"] = "Bearer " + accessToken;
dio.options.headers["Accept"] = "*/*";
try {
response = await dio.get(apiUrl, queryParameters: map);
if (response.statusCode == 200) {
OurClassResponse loginResponse =
OurClassResponse.fromJson(jsonDecode(response.toString()));
yield OurClassSuccess(loginResponse);
}
if (response.statusCode >= 400) {
yield OurClassFailed();
}
} catch (e) {
yield OurClassFailed();
}
}
}
When I make the requests with valid access token, I get 200 status code in bloc class and api works fine.when the token is expired, the dio class correctly gets the new token, make the same api call with new token successfully and inside the below callback I get the correct response also.
onResponse: (response, handler) {
return handler.next(response);
}
but response doesn't comes to bloc class. Though it returned the response by calling return handler.next(response);,it is not coming to response variable inside _getClassCategories method.I expect the correct response should come to the response variable in bloc class for both scenarios:
makes the api call with valid token.
makes the api call with expired token.
but only scenario 1 is working in my code and hope someone here can help me to fix this.
EDIT- this works fine with dio previous version(3.0.10) - code
dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
This line creates a new request with no relation to the original one. If the request succeeds, there is no code listening for a response. If you want the original caller to receive anything, you will need to forward the response to the original handler:
try {
final response = await dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
handler.resolve(response);
} on DioError catch (error) {
handler.next(error); // or handler.reject(error);
}
Also, be sure to forward the error to the handler in non-401 cases as well. Dio 4.0.0 interceptors don't automatically forward anything.

Flutter - Before .then is executed, Function is returning the value and after that reading .then

I am facing 2 problems with the below code and I think both are related.
createFunction is showing an error -
"This function has a return type of 'FutureOr< bool >', but doesn't end with a return statement. Try adding a return statement, or changing the return type to 'void'." - I need to return true or false, so I have to use return type bool.
When the function is executed, it runs smoothly till the PROBLEM AREA (marked in the code). Here it returns null and then comes back to execute .then . I need to run .then right after http.post is executed. At the end of the code it should return true / false.
Any help will be highly appreciated.
Future<bool> createFunction(image) async {
var request = new http.MultipartRequest("POST", Uri.parse(_urlImage));
request.files.add(
await http.MultipartFile.fromPath('imagefile', image));
var response = await request.send().catchError((error) {
throw error;
});
response.stream.transform(utf8.decoder).listen((value) async {
return await http
.post(
_url,
headers: {
'content-type': 'application/json',
'authorization': 'auth'
},
body: json.encode({data}),
)
///// PROBLEM AREA //////
.then((value) async {
final _extractedData = await jsonDecode(value.body);
if (value.statusCode == 201) {
return true;
} else {
return false;
}
}).catchError((error) {
throw error;
});
});
}
Ok, for the next visitors to this page, the correct usage of MultipartRequest class should like this:
var uri = Uri.parse('https://example.com/create');
var request = http.MultipartRequest('POST', uri)
..fields['user'] = 'nweiz#google.com'
..files.add(await http.MultipartFile.fromPath(
'package', 'build/package.tar.gz',
contentType: MediaType('application', 'x-tar')));
var response = await request.send();
if (response.statusCode == 200) print('Uploaded!');

Flutter print any http request automatically - abstract HTTP class

in short words I want to print in my console any Http request that my app is requesting without putting print command after each call I'm making for example :
let's say I have service with http.Client.get and I have another 100 service like that.
what I'm doing now is I'm waiting for the response in each service and then I'm printing it like this print('response is ' + response.body);.
what I want to achieve is that will be automatically be printed out for me without me writing print 100 times in after each request I'm making, any good architect would you recommend to follow ?
hope I cleared the idea well.
You can try the http_interceptor package which allows you to catch all the requests & responses from your http requests (change headers, params etc..)
If you add LogInterceptor, Request and Response URLs and request body are printed. Try ...
final logInterceptor = LogInterceptor(
requestBody: true,
responseBody: true,
error: false,
requestHeader: true,
responseHeader: true);
..interceptors.add(logInterceptor)
well here is my last approach for this.
for every one is seeking for making it with abstraction or let's say wrapping;
first what I did is kind if wrapping for the HTTP class and used my class everywhere instead of the original Http Class.
so the code would go like this
class MHttpClient {
final http.Client client;
final SharedPreferences sharedPreferences;
MHttpClient(this.client, this.sharedPreferences);
Future<http.Response> get(
{String path = "", Map<String, String> extraHeders}) async {
printWrapped('get Path: $path');
final response = await client.get(
Uri.parse(getBaseURL() + Version + path),
headers: getHeaders(extraHeaders: extraHeders),
);
printWrapped("get response : \n" + utf8.decode(response.bodyBytes));
return response;
}
Future<http.Response> post(
{String body = "",
String path = "",
Map<String, String> extraHeders}) async {
printWrapped('sended body: \n');
printWrapped(' ${json.decode(body)}');
final response = await client.post(
Uri.parse(getBaseURL() + Version + path),
body: body,
headers: getHeaders(extraHeaders: extraHeders),
);
printWrapped("post response : \n" + utf8.decode(response.bodyBytes));
return response;
}
Future<http.Response> put({String body = "", String path = ""}) async {
printWrapped('put body: \n ${json.decode(body)}');
final response = await client.put(
Uri.parse(getBaseURL() + Version + path),
body: body,
headers: getHeaders(),
);
printWrapped(utf8.decode(response.bodyBytes));
return response;
}
Future<http.Response> putImage({File image, String path = ""}) async {
printWrapped('Image Path: $path');
final response = await http.put(
Uri.parse(path),
headers: getImageHeaders(),
body: image.readAsBytesSync(),
);
return response;
}
String getBaseURL() {
if (Foundation.kDebugMode)
return BaseURLSTAGING;
else
return BaseURL;
}
String getApiKey() {
if (Foundation.kDebugMode)
return ApiKeyStaging;
else
return ApiKey;
}
String getToken() {
String cashedToken = sharedPreferences.getString(CACHED_TOKEN);
if (cashedToken == null) cashedToken = "";
return cashedToken;
}
Map<String, String> getHeaders({Map extraHeaders}) {
Map<String, String> headers = {
'Content-Type': 'application/json; charset=UTF-8',
'x-api-key': getApiKey(),
HttpHeaders.authorizationHeader: 'Bearer ' + getToken(),
};
if (extraHeaders == null || extraHeaders.isEmpty)
return headers;
else {
headers.addAll(extraHeaders);
return headers;
}
}
Map<String, String> getImageHeaders() {
return <String, String>{'Content-Type': 'image/png'};
}
void printWrapped(String text) {
final pattern = RegExp('.{400}'); // 800 is the size of each chunk
pattern.allMatches(text).forEach((match) => developer.log(match.group(0)));
}
}
and then I used MHttpClient else where
final MHttpClient client;
final response = await client.get(path: path);
and in this case I don't have to warry about anything else ,
and when you need to change one thing you will change it in one place only, and every thing will stay the same and work as you want without braking changes you have to do for all you requested.

Register to aqueduct backend from Flutter frontend

I'm having a bit of difficulty with registering to aqueduct backend from my Flutter frontend
Here is my code in my frontend:
Future<void> signUp(String email, String password) async {
final body = "username:$email,password:$password"; //<- return request entity could not be decoded
//final body = {"username": email, "password": password}; //<- return bad state: Cannot set the body fields of Request with content-type "application/json"
try {
final http.Response response = await http.post(
"http://localhost:8888/register",
headers: {"Content-Type": "application/json"},
body: body);
final jsonResponse = json.decode(response.body);
if (jsonResponse["error"] != null) {
throw HttpException(jsonResponse["error"]);
}
} catch (error) {
throw error;
}
}
There must be some silly mistake. I believe it is with formatting body so I tried 2 options and both throw different http exception (as in comment).
Here is an example of connecting to an Aqueduct server from a Flutter client. (This isn't really a server question, though, since the client and server are independent of each other.)
Here is an example of registering:
void _register(String email, String password) async {
Map<String, String> headers = {"Content-type": "application/json"};
final jsonString = '{"username":"$email", "password":"$password"}';
Response response = await post(YOUR_URL_HERE, headers: headers, body: jsonString);
print('${response.statusCode} ${response.body}');
}
In your example you aren't encoding the JSON correctly.
And here is another example of signing in. The class is a view model architecture that I talk about here.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
class LoginViewModel extends ChangeNotifier {
String _token = '';
bool _isLoggedIn = false;
bool get isLoggedIn => _isLoggedIn;
String get token => _token;
Future onLoginPressed(String username, String password) async {
if (username.isEmpty || password.isEmpty) {
return;
}
_isLoggedIn = await _login(username, password);
notifyListeners();
}
Future<bool> _login(String username, String password) async {
var clientID = 'com.example.app';
var clientSecret = '';
var body = 'username=$username&password=$password&grant_type=password';
var clientCredentials = Base64Encoder().convert('$clientID:$clientSecret'.codeUnits);
Map<String, String> headers = {
'Content-type': 'application/x-www-form-urlencoded',
'authorization': 'Basic $clientCredentials'
};
var response = await http.post(YOUR_URL_HERE, headers: headers, body: body);
final responseBody = response.body;
if (response.statusCode != 200) {
return false;
}
final map = json.decode(responseBody);
_token = map['access_token'];
return true;
}
}