I have a repository class (check below) that calls APIs through the HttpService class. I'm receiving historical data that contains 1000s of data objects. To avoid the computation problem, I'm trying to implement the compute() function to move this computation out of the main thread, but Android Studio is giving a compilation error.
class WealthRepo {
Future<Responser<PerformanceHistoryModel>> fetchPortfolioHistory(
String stackId,
String? duration,
) async {
try {
final resp = await _httpService.makePostRequest(<API_EP>, jsonEncode(<REQUEST_OBJ>));
return Responser<PerformanceHistoryModel>(
message: '',
isSuccess: true,
data: compute(parsePerformanceHistory, resp),
);
} catch (e, st) {
return ErrorHandler.error<PerformanceHistoryModel>(
e,
stackTrace: st,
);
}
}
}
/// Outside WealthRepo class
PerformanceHistoryModel parsePerformanceHistory(Map<String, dynamic> response) {
return PerformanceHistoryModel.fromJson(response);
}
lib/repositories/wealth_repo.dart:1431:23: Error: The argument type
'PerformanceHistoryModel Function(Map<String, dynamic>)' can't be
assigned to the parameter type 'FutureOr
Function(dynamic)'.
'PerformanceHistoryModel' is from 'package:aphrodite_v2/data/models/wealth/performance_history_model.dart'
('lib/data/models/wealth/performance_history_model.dart').
'Map' is from 'dart:core'.
data: compute(parsePerformanceHistory, resp),
^
PS - Responser is a custom response class we created. Not sure how to resolve this issue.
class Responser<T> {
final String message;
final bool isSuccess;
T? data;
Responser({
required this.message,
required this.isSuccess,
this.data,
});
#override
String toString() =>
'Responser(message: $message, isSuccess: $isSuccess, data: $data)';
}
You either need to await your resp before passing it into the compute function.
return Responser<PerformanceHistoryModel>(
message: '',
isSuccess: true, \/\/
data: compute(parsePerformanceHistory, await resp),
);
Or you need to update the signature on the function that compute uses so that it takes in a FutureOr<Map<String, dynamic>>.
Future<PerformanceHistoryModel> parsePerformanceHistory(
FutureOr<Map<String, dynamic>> _response) async {
final response = await _response;
return PerformanceHistoryModel.fromJson(response);
}
Related
To all Dart gurus: I'm trying to implement a generic networking layer in Dart that converts REST service response to a specified model class:
// The idea is to make a network call and get a deserialized model as a response:
final token =
await _authNetworkService.execute<AccessTokenResponse>(request);
Here is the implementation:
// Model interface
abstract class JsonConvertible {
Map<String, dynamic> toJson();
JsonConvertible.fromJson(Map<String, dynamic> json);
}
// Model
class AccessTokenResponse extends JsonConvertible {
String? accessToken;
#override
Map<String, dynamic> toJson() {
return {};
}
#override
AccessTokenResponse.fromJson(Map<String, dynamic> json)
: super.fromJson(json) {
accessToken = json['access_token'];
}
}
// Network response class
class NetworkResponse<Model> {
Model data;
NetworkResponse.ok(this.data);
}
// Class to create a valid network service request
class NetworkRequest {
}
// Class that performs all network calls
class NetworkService {
Future<NetworkResponse<M>> execute<M extends JsonConvertible>(NetworkRequest request) async {
// For simplicity replaced all DIO calls with static data:
final response = {'data': {'access_token': 'XXX'}};
return NetworkResponse.ok(M.fromJson(response['data'])); //<- Fails here with error: Method 'fromJson' isn't defined for the type 'Type'...
}
}
DartPad: https://dartpad.dev/?id=9a29a7e49a084e69fd1d8078d5f2b977
How can I achieve expected behavior?
one way you can solve this is by passing the fromJson constructor as an argument to the execute function but this will add another step for every time execute is called
// Class that performs all network calls
class NetworkService {
Future<NetworkResponse<M>> execute<M extends JsonConvertible>(NetworkRequest request,M Function(Map<String, dynamic>) parser ) async {
// For simplicity replaced all DIO calls with static data:
final response = {'data': {'access_token': 'XXX'}};
return NetworkResponse.ok( parser(response['data']!)); //<- Fails here with error: Method 'fromJson' isn't defined for the type 'Type'...
}
}
and this is how you would call the execute function
final token =
await _authNetworkService.execute<AccessTokenResponse>(request,AccessTokenResponse.fromJson);
I'm getting information from a REST API, but when displaying the information I get the error:
Error: Exception: Expected a value of type 'int', but got one of type 'String'
This is my get method:
Future<List<Requests>> searchRequests() async {
try {
final response = await http.get(
Uri.parse(BaseUrl.baseUrl + 'api/search'));
if (response.statusCode == 200) {
List<Requests> list = parseRequests(response.body);
return list;
} else {
throw Exception("Error");
}
} catch (e) {
throw Exception(e.toString());
}
}
static List<Requests> parseRequests(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<Requests>((json) => Requests.fromJson(json))
.toList();
}
and defined my model like this:
import 'package:flutter_gentelella/models/usuario.dart';
class Requests {
final String id;
final String data1;
final String data2;
final int data3;
const Requests({
required this.id,
required this.data1,
required this.data2,
required this.data3,
});
factory Requests.fromJson(Map<String, dynamic> json) {
return Solicitacoes(
id: json['_id'],
data1: json['data1'],
data2: json['data2'],
data3: json['user']['data3']);
}
Map<String, dynamic> toJson() => {
'_id': id,
'data1': data1,
'data2': data2,
'data3': data3,
};
}
At what point in the code do I convert to int?
I'm trying to generate the order list on the screen but I get the error reported above. I appreciate if someone helps me analyze!
I'd say that the problem is with the field data3: it's the only thing I see in your code defined as an int, and the error is telling you that something was expected as int but it came up as String.
Something like this shouls work (just handle the case in which data3 is not a number appropriately):
factory Requests.fromJson(Map<String, dynamic> json) {
return Solicitacoes(
id: json['_id'],
data1: json['data1'],
data2: json['data2'],
data3: int.tryParse(json['user']['data3']) ?? 'Not a number');
}
I keep getting error such as this, can someone explain the what the error mean and what i need to do to prevent them?
The argument type 'Future<List<CompanyModel>> Function(String)' can't be assigned to the parameter type 'Future<List<CompanyModel>> Function(String?)?'.
I have a ModelClass and i want to get some values from an api to a searchable dropdown widget.
Below is the widget code
DropdownSearch<CompanyModel>(
label: "Name",
onFind: (String filter) async {
var response = await Dio().get(
"http://5d85ccfb1e61af001471bf60.mockapi.io/user",
queryParameters: {"filter": filter},
);
var models = CompanyModel.fromJsonList(response.data);
return models;
},
),
And my model class, it is quiet long.
class CompanyModel {
final String name;
CompanyModel({required this.name});
factory CompanyModel.fromJson(Map<String, dynamic> json) {
return CompanyModel(name: json['comapny_name']);
}
static List<CompanyModel> fromJsonList(List list) {
return list.map((item) => CompanyModel.fromJson(item)).toList();
}
///this method will prevent the override of toString
String userAsString() {
return '#${this.name}';
}
///custom comparing function to check if two users are equal
bool isEqual(CompanyModel model) {
return this?.name == model?.name;
}
#override
String toString() => name;
}
The error says that the onFind attribute requires a function with a nullable String as its parameter, you are not passing a nullable String
Try changing this
onFind: (String filter) async {
to this
onFind: (String? filter) async {
if this doesn't work you will have to add the code of DropdownSearch class so we can get more info
I tried to extract future from a steam, but he made an error
Return stream is good
Future<T> post<T>(options){
return _request(options["url"],"POST",data: options["data"]).single;
}
Stream<T> _request<T>(url,method,{data}) {
return Stream.fromFuture(ApiDio().request(url,method: method,data: data))
.transform(ResponseTransformer());
}
fix:
Future<T> post<T>(options){
return _request<T>(options["url"],"POST",data: options["data"]).single;
}
The first method should add generic
Your Class declaration will be something like this:
class YourClass {
String name;
YourClass(this.name);
factory YourClass.fromJson(Map<String, dynamic> json) {
return YourClass(json['name'] as String);
}
}
To convert it from your HttpResponse:
final response = await http.get(url, headers: await setHeaders());
dynamic parsed = jsonDecode(response.body);
final yourClassObject = YourClass.fromJson(parsed);
I am trying to user Dio Client for making API calls. While I receive the response It throws an error
'_InternalLinkedHashMap' is not a subtype of type 'String'
Trying to resolve it but I can't. Below is the code
Future<dynamic> get(
String uri, {
Map<String, dynamic> queryParameters,
Options options,
CancelToken cancelToken,
ProgressCallback onReceiveProgress,
}) async {
try {
final Response response = await _dio.get(
uri,
queryParameters: queryParameters,
options: options,
cancelToken: cancelToken,
onReceiveProgress: onReceiveProgress,
);
return response.data;
} catch (e) {
print(e.toString());
throw e;
}
}
}
Post Api Call
Future<News> getPosts() async {
try {
final res = await _dioClient.get(Endpoints.getPosts);
return News.fromJson(res);
} catch (e) {
print(e.toString());
throw e;
}
}
Model class uses built_value
abstract class News implements Built<News, NewsBuilder> {
News._();
factory News([updates(NewsBuilder b)]) = _$News;
#BuiltValueField(wireName: 'status')
String get status;
#BuiltValueField(wireName: 'totalResults')
int get totalResults;
#BuiltValueField(wireName: 'articles')
BuiltList<Articles> get articles;
String toJson() {
return json.encode(serializers.serializeWith(News.serializer, this));
}
static News fromJson(String jsonString) {
return serializers.deserializeWith(
News.serializer, json.decode(jsonString));
}
static Serializer<News> get serializer => _$newsSerializer;
}
Serializer class
#SerializersFor([
News,
Articles,
Source,
])
final Serializers serializers = (_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
help me how to solve it
You need to change 2 things
1) use jsonSerializers instead of default serializers in your built_value.
2 )use response.toString() instead of data in dio. Here a working example from me, just copy the parts you need.
Built Value Model
abstract class TravelRequest implements Built<TravelRequest, TravelRequestBuilder> {
TravelRequest._();
factory TravelRequest([updates(TravelRequestBuilder b)]) = _$TravelRequest;
#nullable
#BuiltValueField(wireName: 'id')
int get id;
#nullable
#BuiltValueField(wireName: 'country_from')
String get countryFrom;
#nullable
#BuiltValueField(wireName: 'traveler_name')
String get travelerName;
#nullable
#BuiltValueField(wireName: 'traveler_email')
String get travelerEmail;
#nullable
#BuiltValueField(wireName: 'description')
String get description;
String toJson() {
return json.encode(jsonSerializers.serializeWith(TravelRequest.serializer, this));
}
static TravelRequest fromJson(String jsonString) {
return jsonSerializers.deserializeWith(TravelRequest.serializer, json.decode(jsonString));
}
static Serializer<TravelRequest> get serializer => _$travelRequestSerializer;
}
How to use with Dio
Future createTravelRequest(TravelRequest travelRequest) {
return _apiProvider.dio.post(rootPath, data: travelRequest.toJson()).then((response) {
travelRequests.add(TravelRequest.fromJson(response.toString()));
});
}