I'm trying out things with Flutter right now. But my variables keep getting reinitialised when accessed from another class.
I'm using json parsing and i need two parts of my request. The "Relatorio" part and the "Mensagem" part.
to parse this json i'm doing this:
List<RelatorioProdutos> parseRelatorioPorProduto(String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
print (mensagem); // Here the variable returns well,
//but when i need it in other class i receive null.
return relatorio.map<RelatorioProdutos>((json) => new RelatorioProdutos.fromJson(json)).toList();
}
class RelatorioProdutos {
String CodigoProduto;
var QtdVendida;
var TotalVendas;
String Descricao;
RelatorioProdutos({this.CodigoProduto, this.QtdVendida, this.TotalVendas, this.Descricao,});
factory RelatorioProdutos.fromJson(Map json) {
//returns a List of Maps
return new RelatorioProdutos(
CodigoProduto: json['CodigoProduto'] as String,
QtdVendida: json['QtdVendida'],
TotalVendas: json['TotalVendas'],
Descricao: json['Descricao'] as String,
);
}
}
I want to use this 'mensagem' variable in another class to show the error for user, but i always receive 'null'.
i already tried setState but it reloads my json and i dont want to request the RestServer again.
Thanks from now!
If I understand correctly, you want to access a local variable of a function from another class. I don't think it's possible.
One way to do it, would be to wrap your response in another object containing the response, and this variable:
List<Response<RelatorioProdutos>> parseRelatorioPorProduto(
String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
print(mensagem); // Here the variable returns well,
//but when i need it in other class i receive null.
return relatorio
.map((json) => new Response<RelatorioProdutos>(
new RelatorioProdutos.fromJson(json), mensagem))
.toList();
}
class RelatorioProdutos {
String CodigoProduto;
var QtdVendida;
var TotalVendas;
String Descricao;
RelatorioProdutos({
this.CodigoProduto,
this.QtdVendida,
this.TotalVendas,
this.Descricao,
});
factory RelatorioProdutos.fromJson(Map json) {
//returns a List of Maps
return new RelatorioProdutos(
CodigoProduto: json['CodigoProduto'] as String,
QtdVendida: json['QtdVendida'],
TotalVendas: json['TotalVendas'],
Descricao: json['Descricao'] as String,
);
}
}
class Response<T> {
const Response(
this.value,
this.errorMessage,
);
final T value;
final String errorMessage;
bool get hasError => errorMessage != null;
}
In this example I created a Response object that can contains both the response value and an error message.
In the parseRelatorioPorProduto, instead of returning the relatorio, I changed the return type to Response<RelatorioProdutos> in order to have access to the value and the error message from any class which call this function.
Thanks Letsar, i tried yout ideia but i get a lot of others erros.
To solve this problem i used this:
List<RelatorioProdutos> parseRelatorioPorProduto(String responseBody) {
final parsed = json.decode(responseBody);
var relatorio = parsed['Relatorio'];
var mensagem = parsed['Mensagem'];
if(mensagem[0].toString().substring(16,17) == "0"){
List<RelatorioProdutos> asd = new List();
RelatorioProdutos aimeudeus = new RelatorioProdutos(Descricao: mensagem[0].toString(), CodigoProduto: "a", TotalVendas: 0, QtdVendida: 0);
asd.add(aimeudeus);
return asd;
}else{
return relatorio.map<RelatorioProdutos>((json) => new RelatorioProdutos.fromJson(json)).toList();
}
}
Related
I'm a really fresh new dev in flutter, I'm trying to filter results in a json response based on the title.
And i would like to use the compute method. This method only allow 2 arguments, so it's why i think i need to use a map to get the response.body and the query for filtering.
This is my HttpService :
class HttpService {
static List<Post> _parsePosts(Map map) {
print(map);
return map["body"].map((json) => Post.fromJson(json)).where((post) {
final titleLower = post.title.toLowerCase();
final queryLower = map["query"].toLowerCase();
return titleLower.contains(queryLower);
}).toList();
}
static Future<List<Post>> fetchPosts(String query) async {
final uri = Uri.parse('https://jsonplaceholder.typicode.com/posts');
final response = await retry(
() => http.get(uri).timeout(Duration(seconds: 10)),
retryIf: (e) => e is SocketException || e is TimeoutException,
);
Map posts = new Map();
posts["body"] = json.decode(response.body);
posts["query"] = query;
if (response.statusCode == 200) {
return compute(_parsePosts, posts);
} else {
throw Exception("Failed to load posts ${response.statusCode}");
}
}
}
The print(map); contains the map with values -> https://prnt.sc/131ldey
But the problem is reported on : return map["body"].map((json) => Post.fromJson(json)).where((post) {
with : _TypeError (type '(dynamic) => dynamic' is not a subtype of type '(dynamic) => bool' of 'test')
I really don't understand what is the reason of this error..
Your titleLower is not casted into a String type! So the contains method returns a dynamic value since it does not appear as the contains method of the String type for the VM !
final String myValue = ...;
return myValue.contains(‘a’);
I want to send a List of ManageTagModel in a multipart request along with other models and files..
I am not certain of how to send this List of model..
This is my code for sending the multipart request without the List:
var uri = Uri.parse(...);
final request = http.MultipartRequest('Post', uri);
request.fields['Id'] = '3';
request.fields['Name'] = siteModel.name;
request.fields['MapAddress'] = siteModel.mapAddress;
request.fields['Country'] = siteModel.country;
request.fields['City'] = siteModel.city;
request.fields['CategoryNb'] = siteModel.categoryNb;
request.fields['UserId'] = userId;
request.fields['Caption'] = caption;
for (File i in
multipleFiles) {
final mimeTypeData =
lookupMimeType(i.path, headerBytes: [0xFF, 0xD8]).split('/');
print("IMAGE: " + i.path);
// Attach the file in the request
final file = await http.MultipartFile.fromPath('files', i.path);
print(mimeTypeData[0] + " mimeTypeData[0]");
print(mimeTypeData[1] + " mimeTypeData[1]");
request.files.add(file);
this is my model:
import 'dart:convert';
class ManageTagModel {
String posX;
String posY;
String postOrder;
String tagger;
String tagged;
ManageTagModel(
{this.posX, this.posY, this.postOrder, this.tagger, this.tagged});
//Flutter way of creating a constructor
factory ManageTagModel.fromJson(Map<String, dynamic> json) => ManageTagModel(
posX: json['PosX'],
posY: json['PosY'],
postOrder: json['PostOrder'],
tagged: json['Tagged'],
tagger: json['Tagger']);
Map<String, dynamic> toMap() {
return {
"PosX": posX,
"PosY": posY,
"PostOrder": postOrder,
"Tagger": tagger,
"Tagged": tagged
};
}
}
List<ManageTagModel> fromJson(String jsonData) {
// Decode json to extract a map
final data = json.decode(jsonData);
return List<ManageTagModel>.from(
data.map((item) => ManageTagModel.fromJson(item)));
}
String toJson(ManageTagModel data) {
// First we convert the object to a map
final jsonData = data.toMap();
// Then we encode the map as a JSON string
return json.encode(jsonData);
}
List encodeToJson(List<ManageTagModel> list) {
List jsonList = List();
list.map((item) => jsonList.add(item.toMap())).toList();
return jsonList;
}
My backend c# method has a parameter List
Any help is appreciated!!
I'm pretty sure I'm quite late here and you might have already found a solution. I have gone through multiple threads and didn't actually find any answers but discovered myself out of frustration and thought to myself that the answer actually is still not out there for any other lost human soul. So here is my solution for anyone still stuck here which is quite intuitive.
You simply have to add all the elements of the list to the request as "files" instead of "fields". But instead of fromPath() method, you have to use fromString().
final request = http.MultipartRequest('Post', uri);
List<String> ManageTagModel = ['xx', 'yy', 'zz'];
for (String item in ManageTagModel) {
request.files.add(http.MultipartFile.fromString('manage_tag_model', item));
}
This worked out for me and I hope it works for you too.
if the data was not string
for (int item in _userData['roles']) {
request.files
.add(http.MultipartFile.fromString('roles', item.toString()));
}
How can put a Set of object to Json. Seems like that code is not good at all
Future<File> writeBasket(Set<Item> listItems) async {
final file = await _localFile;
var jsonString = listItems.map((Item item) {
return jsonEncode(item);
});
return file.writeAsString(jsonString.toString());
}
Make sure that Item has a toJson method, for example:
class Item {
String val1;
Item(this.val1);
Map<String, dynamic> toJson() => {'val1': val1};
}
The nearest equivalent to a set in json is a list, so convert your set to a list before calling json.encode.
Set<Item> items = Set<Item>()..add(Item('xxx'))..add(Item('yyy'));
String j = json.encode(items.toList());
print(j);
prints [{"val1":"xxx"},{"val1":"yyy"}] which you could save to your file.
I'm having an issue with calling my data from a json file. When I click a button to have it appear in a textarea, it does nothing for the first click, but works like expected after that. What the program does is gets an id from dashboard and based on that id grabs different json file to pull in.
The program shows an error of:
ERROR TypeError: Cannot read property 'data' of undefined
at StepService.webpackJsonp.102.StepService.populateList (step.service.ts:69)
at CalibrationDetailComponent.webpackJsonp.101.CalibrationDetailComponent.next
step.service.ts
private jsonData: any; //Json data
public list: String[] = []; //Holds the list of steps
public listLength: number; //Length of the list
public listCurrent: number = 0; //Current step the list is on
//Gets the json file
public getJson(): Observable<any> {
return this.http.get(this.jsonUrl)
.map(response => response.json());
}
//Subscribe
public subScribe2Json() {
this.getJson().subscribe(data => (this.jsonData = data));
}
//Populates the list from the json so I can pull out specific steps
public populateList() {
this.jsonData.data.forEach(element => { //The line that throws the error
this.list.push(element.name);
});
this.listLength = this.list.length;
}
//Returns the mainStepText with the current step
public getJsonData(): String {
this.mainStepText = this.list[this.listCurrent];
return this.mainStepText;
}
calibration-detail.component.ts
next button method
next() { //Advances step
this.stepService.subScribe2Json();
if (this.stepService.listCurrent < 1) { //Makes sure only runs once to populate the list
this.stepService.populateList(); //Populates list from the json array
}
if (this.stepService.listCurrent < this.stepService.listLength) { //make sure dont go past number of steps
this.stepService.subScribe2Json(); //Sub to list
this.mainStepText = this.stepService.getJsonData(); //Grab the data from the list and output to main textarea
this.stepService.listCurrent ++; //Increments the step
This is not a complete solution but an answer to what the problem is. And direct your thought into the right direction depending on what you want to achieve.
You call
this.stepService.subScribe2Json();
if (this.stepService.listCurrent < 1) {
...
this calls the first method and immediately the second without waiting for the data. And then of course it fails because it is not there yet.
Depending on your use case you could either return the Observable (maybe change it to a Promise,... not 100% sure) and then:
return this.getJson().subscribe(data => (this.jsonData = data));
and something like
this.stepService.subScribe2Json().then(/* do all stuff here */);
or initialize
private jsonData: any = [];
but here of course you don't have anything on the first run.
I know that it is possible to create an instance from a symbol like is shown in this link:
Create an instance of an object from a String in Dart?
But this doesn't work for me since what I want to do is create an instance without having the class.
This problem is caused because I have One class with an internal List:
class MyNestedClass {
String name;
}
class MyClass {
int i, j;
String greeting;
List<MyNestedClass> myNestedClassList;
}
And I want to convert a map to this class:
{
"greeting": "hello, there",
"i": 3,
"j": 5,
"myNestedClassList": [
{
"name": "someName1"
},{
"name": "someName2"
}
]
}
right now I am doing something like this:
static void jsonToObject(String jsonString, Object object) {
Map jsonMap = JSON.decode(jsonString); //Convert the String to a map
mapToObject(jsonMap, object); //Convert the map to a Object
}
static void mapToObject(Map jsonMap, Object object) {
InstanceMirror im = reflect(object); //get the InstanceMirror of the object
ClassMirror cm = im.type; //get the classMirror of the object
jsonMap.forEach((fieldNameStr, fieldValue) { // For each element in the jsonMap
var fieldName = new Symbol(fieldNameStr); // convert the fieldName in the Map to String
if (isPrimitive(fieldValue)) { // if fieldValue is primitive (num, string, or bool
im.setField(fieldName, fieldValue); //set the value of the field using InstanceMirror
} else if (fieldValue is List) { // else if the fieldValue is a list
ClassMirror listCm = (cm.declarations[fieldName] as VariableMirror).type; //get the class mirror of the list
var listReflectee = listCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
for(var element in fieldValue) { //for each element in the list
if(!isPrimitive(element)) { // if the element in the list is a map (i.e not num, string or bool)
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
//This is the line that doesn't work correctly
//It should be something like:
//
// ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
//
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
mapToObject(element, listObject); //convert the element to Object
}
listReflectee.add(element); //add the element to the list
};
} else { //else (the field value is a map
ClassMirror fieldCm = (cm.declarations[fieldName] as VariableMirror).type; // get the field ClassMirror from the parent declarations
var reflectee = fieldCm.newInstance(const Symbol(''), []).reflectee; //create an instance of the field
mapToObject(fieldValue, reflectee); // convert the fieldValue, which is a map, to an object
im.setField(fieldName, reflectee); // set the value of the object previously converted to the corresponding field
}
});
}
As you can see the lines that are not actually working are:
var listType = listCm.typeArguments[0]; //get the TypeMirror of the list (i.e MyNestedClass from List<MyNestedClass>)
var listObject = (listType as ClassMirror).newInstance(const Symbol(''), []); //create an instance of the specified listType
since they are creating an instance on localClassMirror and not on MyNestedClass. I'm looking for a method similar to:
ClassMirror.fromSymbol(listType.simpleName).newInstance(const Symbol(''), []);
you can see the full source code in the next URL:
DSON Source Code
If you have the full qualified name of the class you should be able to find the type using libraryMirror and then it should be similar to your linked question to create an instance.
I haven't done this myself yet and have not code at hand.
see also: how to invoke class form dart library string or file
An alternative approach would be to create a map at application initialization time where you register the supported types with their name or an id and look up the type in this map (this is like it's done in Go)