http requests are very slow in flutter - flutter

I am using the http package to call my API, but every request takes 8+ seconds to complete.
I have tried calling the same route via browser and postman and I get the response in less than a second. Also, I can assure that there is no issue with my internet connection.
class ApiRest {
Future<List<Product>> getProducts() async {
final apiResponse = await http.get(Uri.parse('some route'));
final resBody = jsonDecode(apiResponse.body);
return resBody['products']
.map<Product>((product) => Product.fromJson(product))
.toList();
}
}
Additionally, I tried applying the approach recommended by this answer, using the HTTP Client(), which didn't make a difference either.
Is there anything I am doing wrong or inefficient?
Lastly, the latest version of the http package for Flutter is currently 0.13.4. Do you think that the issue might be with this package? Or it might not be stable enough?

Related

Force http from dart.dev to use dio from flutterchina.club

I have built interceptors around dio for error handling,logging and cache. I found some other packages like cached_network_image, advance_pdf_viewer use dart.dev's http client.
I want to make same requests processed from cached_network_image, advance_pdf_viewer from dio's client so that custom error messages are displayed in UI which provides uniformity.
Future<Dio> _getDio() async {
final dio = Dio()..options.baseUrl = ApiEndPoints.base;
dio.interceptors.addAll([
DioCachingInterceptor(
connectivity,
),
DioAppInterceptor(),
DioLoggingInterceptor(),
]);
return dio;
}
Any Ideas??

Flutter testing, passing an actual HTTP client, instead of a mocked one

I have developed a package in flutter, and wanted to test it, which makes a network call.
As we know that all network request while testing will return 404, and such HTTP reqeust needs to be mocked.
However its also possible to use the orginal HTTP clients instead of mocking or getting 404.
https://github.com/flutter/flutter/issues/19086#issuecomment-402639134
How do we do that ?
I have tried this :
main(){
TestWidgetsFlutterBinding.ensureInitialized();
HttpOverrides.runZoned(() {
test("Case1: Make HTTP request to an actual server", ()async{
let a = MyPackage.makesAHTTPRequest();
expect(a,"hello world");
});
}, createHttpClient: (SecurityContext c) => new HttpClient(context: c));
}
My URL is working all fine.
But it keeps giving me 404.
How do one use real HTTP client, if needed that way?
Ok so if any one is facing a similar issue like me use this hack.
You will need to modify your class, in a way that we can inject HTTP clients into it at run time. We will need to modify our test case as such.
import 'package:http/http.dart'; //client is from this pack
Client httpclinet = Client();
var a = MyPackage.makesAHTTPRequest(httpclient);
remove that Httpoverride.runzoned cod, you can pass Client object from http package directly.
Some test case will fail, due to fake asynchronous effect, but you can use timeouts to manage those.
You will also need to remove any such statements:
TestWidgetsFlutterBinding.ensureInitialized();
In my case I added this line as I was loading files from assets, using packages notation, I referenced them locally and removed above ensureInitalized line as well. [Actually I passed flag to use local notation during testing and package notation otherwise]

Flutter call http requests from a unit\widget test without mocking

I have an app that follows MVC+service architecture. The service layer makes the http requests for rest APIs. However the response of the http requests change intermittently which cause my models to change or else random crashes in my app. SO to capture the change in these APIs I want to write some automated tests which can tell me exactly what changed. A sample test case is as following:
test("login_valid", () async {
final loginData = LoginData(
email: "abc#gmail.com",
password: "123"
);
final parameters = loginData.toJson();
var json = await httpService.post(parameters);
var loginResponse = LoginResponse.fromJson(json);
expect(loginResponse.status, "OK");
});
However, the above code throws SocketException upon run. I know this exception is thrown when INTERNET permission is not given in AndroidManifest.xml but I don't know how to set this for unit\widget tests.
P.S. I can't mock the service layer using mockit or similar framework because the whole point is to test my service layer which doesn't have any business logic but just provides network integration.
Any solution or suggestion will be really helpful. I am okay with other approaches to achieve the same intent also, if there are any.
Check this or the gihub issue discussion pointed there
https://timm.preetz.name/articles/http-request-flutter-test

Protractor not wait for https.request callback

I hit a Protractor issue, if I run this within "ts-node demo.js" it works well and can output the response code and response body.
But if I run this in Protractor it block, then can't get the expected output response code and body, seems like Protractor won't wait for the callback.
In this situation, how can I output the response code and boday?
it(Check manifests V2 api, async() => {
const https = require('https')
const options = {
hostname: 'demo-quayecosystem-quay-quay.com',
port: 443,
path: '/v2/quay/multiarchdemo/manifests/latest',
method: 'GET',
headers: {
'Accept': 'application/vnd.docker.distribution.manifest.list.v2+json'
}
}
https.request(options, res => {
browser.getTitle().then(()=>{
console.log("starting.........");
console.log(`statusCode: ${res.statusCode}`)
res.on('data', d => {
process.stdout.write(d)
})
})
})
})
The function above is an async and when you make your function async, then you should use the await keyword in front of the https.request or return it like this.
return https.request...
And another thing that could lead to problems are...
https.request is making a direct HTTP request, without using a browser
broser.getTitle() is using the browser to interact with the web page.
Be aware - browser uses the browser, and http.request uses direct HTTP from node.js - these are two different things. And it will lead to unpredictable things to mix them. So consider if you want to "test as a user" and then use the browser, or if you want to do the fastest possible test and "test as a website or javascript" and use HTTP.request.
Try reading the async/await page on the Protractor website.
https://www.protractortest.org/#/async-await
If you are puzzled about async/await consider seeing the video from Fun Fun Function on promises https://www.youtube.com/watch?v=568g8hxJJp4&t=251s.

Why (usually) there's a repository layer on BLoC pattern?

I'm new to Flutter and have just heard the BLoC concept from reading tutorials about Flutter. From this tutorial, I heard BLoC for the first time. But I also see a file called "Repository" in this article. Basically the data flow goes like this:
Web API --> Api Provider --> Repository --> BLoC --> Widget
What I don't understand is that why there's a need for the Repository layer, as when I look at the repository file, it's basically just returning the API Provider's Future result? I got curious and try to search further, and I see some of the people's coding patterns on the internet also has a Repository layer on it.
In the original article, the API Provider does everything. It calls the get request, it awaits for the Future resolve, it converts the JSON data into appropriate model, and return the model enclosed with Future.
class ApiProvider {
Future<ItemModel> fetchMovieList() async {
final response = await client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
if (response.statusCode == 200)
return ItemModel.fromJson(json.decode(response.body));
else
throw Exception('Failed to load post');
}
}
class Repository {
ApiProvider _api = ApiProvider();
Future<ItemModel> fetchMovieList() => _api.fetchMovieList(); // why?
}
class Bloc {
Repository _repository = Repository();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async {
ItemModel itemModel = await
_repository.fetchAllMovies();
_moviesFetcher.sink.add(itemModel);
}
}
Currently I modify it so that the Api Provider returns pure Future, where the Repository implement the .then() and convert the response into appropriate data, but I tend to avoid await because in React Native await causes the app to look unresponsive. I also move error checking into BLoC.
class ApiProvider {
Future fetchMovieList() => client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
}
class Repository {
ApiProvider _api = ApiProvider();
Future<ItemModel> fetchMovieList() => _api.fetchMovieList().then(response => ItemModel.fromJson(json.decode(response.body));
}
class Bloc {
Repository _repository = Repository();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async => _repository.fetchPopularMovies().then((response) => _moviesFetcher.sink.add(response))
.catchError((onError) => throw Exception("Failed to load post $onError"));
}
But still, I feel like this is a stretch to justify the need for this Repository layer. If I can, I want to make it like this:
class ApiProvider {
Future<ItemModel> fetchMovieList() => client.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey")
.then(response => ItemModel.fromJson(json.decode(response.body));
}
class Bloc {
ApiProvider _api = ApiProvider();
final _moviesFetcher = PublishSubject<ItemModel>();
Observable<ItemModel> get allMovies => _moviesFetcher.stream;
fetchAllMovies() async => _api.fetchPopularMovies().then((response) => _moviesFetcher.sink.add(response))
.catchError((onError) => throw Exception("Failed to load post $onError"));
}
and get rid of the Repository layer altogether. I'm not trying to say the Repository layer is unnecessary, but right now I don't know what pattern problem the Repository layer trying to solve. I just want to know why there's a Repository layer in the first place and what the real-world significant use case of Repository. I know this question may be flagged as a question that can trigger discussion instead of straight answers. But I believe there is some kind of narrowed answers for this question. I just can't find it when I tried to search on the internet (the search result got mixed up with other uses of "Repository" terms, like git and subversion).
Ok, forget about it. I found this excellent article that explains that basically Repository is to abstract where the data is coming from, whether it's from disk cache, cloud, or other source. The factory will decide what source to use based on the each source availability. The caller will just only need to go through one gate. Because the tutorial above has only one source (API/cloud), it looks useless to me at that moment.
Here is an excellent summary of the why. And it makes complete sense. This is from the BLoC documentation, where they detail a weather app tutorial that uses a Repository layer (see here for the full article).
"The goal of our repository layer is to abstract our data layer and
facilitate communication with the bloc layer. In doing this, the rest
of our code base depends only on functions exposed by our repository
layer instead of specific data provider implementations. This allows
us to change data providers without disrupting any of the
application-level code. For example, if we decide to migrate away from
metaweather, we should be able to create a new API client and swap it
out without having to make changes to the public API of the repository
or application layers."
I'm going for it!