How to create a post request in flutter - GetConnect - flutter

I am creating a flutter app using GetX, for making server connections I am using GetConnect, I successfully integrated the Get Request but I am unable to integrate Post Request
Here is the piece of code:
const _baseUrl = 'https://support.instagram.com/'; // Dummy api url and key
const Map<String, String> _mapHeaders = {
"ISG-API": "ZMWFDK83NMDF7NM5DF23FI0DBUJ"
};
class ApiService extends GetConnect {
Future<TicketReply> submitTicketReply(String ticketId, String tktreply) async {
String apiUrl = '${_baseUrl}/supportreply';
var body = {
'tktid': ticketId,
'tktreply': tktreply,
};
final response = await post(
apiUrl,
body,
headers: _mapHeaders,
);
print('response: ${response.body}');
if (response.statusCode == 200) {
return TicketReply.fromJson(response.body);
} else {
return Future.error(response.statusText!);
}
}
}
Kindly guide me how to make a successful post request using getconnect

You should add the decoder to the request like so
await post(
apiUrl,
body,
headers: _mapHeaders,
decoder: (resbody) => TicketReply.fromJson(resbody),
);

Related

How to properly make a api request in flutter?

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?

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 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.

How to cache the response of API calls in Flutter?

I am working in Flutter App to get the items from API. I want to cache the API response for 12 hours. Every 12 hours the response will be changed. Once the 12 hours completed then I need to fetch it from Internet. I used the below code to get it from internet.
Future<List<Playlist>> fetchPlaylistByChannelId({String channelId}) async {
Map<String, String> parameters = {
'part': 'snippet,contentDetails',
'channelId': channelId,
'maxResults': '10',
'key': API_KEY,
};
Uri uri = Uri.https(
_baseUrl,
'/youtube/v3/playlists',
parameters,
);
Map<String, String> headers = {
HttpHeaders.contentTypeHeader: 'application/json',
};
// Get Playlist details
var response = await http.get(uri, headers: headers);
if (response.statusCode == 200) {
var data = json.decode(response.body);
List<dynamic> playListJson = data['items'];
// Fetch all play list
List<Playlist> playLists = [];
playListJson.forEach(
(json) => playLists.add(
Playlist.fromMap(
json["id"],
json["snippet"],
json["contentDetails"],
),
),
);
return playLists;
} else {
throw json.decode(response.body)['error']['message'];
} }
Please help me out this.
Include flutter_cache_manager in pubspec.yaml.
Now define a cache manager
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
// Custom Implementation of CacheManager
// by extending the BaseCacheManager abstract class
class MyCacheManager extends BaseCacheManager {
static const key = "customCache";
static MyCacheManager _instance;
// singleton implementation
// for the custom cache manager
factory MyCacheManager() {
if (_instance == null) {
_instance = new MyCacheManager._();
}
return _instance;
}
// pass the default setting values to the base class
// link the custom handler to handle HTTP calls
// via the custom cache manager
MyCacheManager._()
: super(key,
maxAgeCacheObject: Duration(hours: 12),
maxNrOfCacheObjects: 200,
fileFetcher: _myHttpGetter);
#override
Future<String> getFilePath() async {
var directory = await getTemporaryDirectory();
return path.join(directory.path, key);
}
static Future<FileFetcherResponse> _myHttpGetter(String url,
{Map<String, String> headers}) async {
HttpFileFetcherResponse response;
// Do things with headers, the url or whatever.
try {
var res = await http.get(url, headers: headers);
// add a custom response header
// to regulate the caching time
// when the server doesn't provide cache-control
res.headers.addAll({'cache-control': 'private, max-age=120'});
response = HttpFileFetcherResponse(res);
} on SocketException {
print('No internet connection');
}
return response;
}
}
Now use
class HttpProvider {
Future<Response> getData(String url, Map<String, String> headers) async {
var file = await MyCacheManager().getSingleFile(url, headers: headers);
if (file != null && await file.exists()) {
var res = await file.readAsString();
return Response(res, 200);
}
return Response(null, 404);
}
}
Details at https://referbruv.com/blog/posts/caching-get-request-calls-using-flutter-cache-manager and https://proandroiddev.com/flutter-lazy-loading-data-from-network-with-caching-b7486de57f11
UPDATE: flutter_cache_manager 2.0.0
There is no longer a need to extend on BaseCacheManager, you can directly call the constructor. The BaseCacheManager is now only an interface. CacheManager is the implementation you can use directly.
check here
Another way of caching is by using hive a No-SQL database it is faster to retrieve documents and is easy to use. And when users come online just refresh the data in hive
For more details check:https://github.com/shashiben/Anime-details to know how to cache using hive

How To deal with Response after post request dart httpClient

So I was having issues with flutter http package when it came to making a post request so I used dart HttpClient. I made a post request according to what was described somewhere but I am having issues getting response. Here is my code
Future<HttpClientResponse> submit() async {
print('start');
Map<String, dynamic> data = { 'title' : 'My first post' };
String jsonString = json.encode(data); // encode map to json
String paramName = 'param'; // give the post param a name
String formBody = paramName + '=' + Uri.encodeQueryComponent(jsonString);
List<int> bodyBytes = utf8.encode(formBody); // utf8 encode
HttpClientRequest request =
await HttpClient().postUrl(Uri.parse('https://jsonplaceholder.typicode.com/posts'));
// it's polite to send the body length to the server
request.headers.set('Content-Length', bodyBytes.length.toString());
request.headers.set('Content-Type', 'application/json');
request.add(bodyBytes);
print('done');
return await (request.close());
}
How do I get the response from this request?
HttpClientResponse response = await request.close();
response.transform(utf8.decoder).listen((contents) {
print(data); // <- response content is here
});
This will return HttpCLientResponse, more info https://api.dartlang.org/stable/2.6.1/dart-io/HttpClient-class.html
I have found this from the docs
new HttpClient().get('localhost', 80, '/file.txt')
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
response.transform(utf8.decoder).listen((contents) {
// handle data
});
});
Or Use http library
I have create a common method which can handle all get Request,
Future<String> getRequest([var endpoints, var queryParameters]) async {
var uri = Uri.https(NetworkUrl.BASE_URL_1, endpoints, queryParameters);
uri.replace(queryParameters: queryParameters);
var response =
await http.get(Uri.encodeFull(uri.toString()));
//Retrun reponse here
if (response.statusCode == 200) return response.body;
}
To get a response from the above method,
Future<String> deletePostApi() async {
await NetworkRepository()
.getRequest(NetworkUrl.deletePost + '${widget.mFeedData.post_id}')
.then((value) {// <=value is json respone
var dataConvertedToJSON = json.decode(value);
print("checkEmailResp" + dataConvertedToJSON.toString());
});
}