Trying to display in Flutter a result I am receiving from my Node.JS server via MySQL query:
[{"NAME":"Matematicas"},
{"NAME":"Naturales"},
{"NAME":"Ciencias Sociales"},
{"NAME":"Lenguaje"},
{"NAME":"Religion"}]
This is the class I am using in Flutter to handle it:
class Subject {
final String name;
Subject({
required this.name,
});
factory Subject.fromJson(Map<String, dynamic> json) {
return Subject(name: json['NAME']);
}
}
This is the method from which I obtain the data:
Future<Subject> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return Subject.fromJson(jsonDecode(response.body));
}
This is my initState
void initState() {
super.initState();
futureSubject = fetchSubject();
}
This is my Widget build piece:
Widget build(BuildContext context) {
return FutureBuilder<Subject>(
future: fetchSubject(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('Error'),
);
} else if (snapshot.hasData) {
return Scaffold(
appBar: AppBar(
title: Text('Materias'),
backgroundColor: Colors.green[300],
actions: [
Padding(
padding: EdgeInsets.only(right: 3.0),
child: IconButton(
icon: Icon(Icons.logout),
//TODO llamar funcion logout
onPressed: () {},
iconSize: 26,
),
)
],
),
body: Text(snapshot.data!.name));
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
This is what I get:
Uncaught (in promise) Error: Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'
I just want to display the information I am receiving as a List or table like fashion. Any ideas on what and how to refactor this?
Its happened because your return data is an array. Try this
final data = json.decode(response.body);
return List<Subject>.from(data.map((value) => Subject.fromJson(value)));
It looks like the fetchSubject method needs to be modified and the widget itself. The data you displayed is a List of objects, thus the error that you are trying to see type Map<String, dynamic> from jsonDecode(response.body) but it returns a List<dynamic> instead. Thus, you need to modify fetchSubject and get a List<Subject from your API not just an object. Or, you need to update an API. Just as an example (haven't tested it but should work):
Future<List<Subject>> fetchSubject() async {
var prefs = await SharedPreferences.getInstance();
var token = prefs.getString('token');
var response = await http.get(Uri.parse('http://localhost:8000/subjects'),
headers: {'x-access-token': token!});
print(response.body);
return jsonDecode(response.body).map((item) => Subject.fromJson(item));
}
and change all logic to handle a List of Subject and not just Subject. The JSON your API returns is a list (array) of objects, not just an object.
Related
I've spent around an hour looking for this solution somewhere online. I'm new to flutter & dart languages but I'm very comfortable with C# and .net. Even tho dart/flutter use C# syntax a lot of the language feels much different than I thought it would.
I have a restful API in .net which returns a json object of String : String and String : [Array of Strings]. I have an object class within flutter where I can deserialize the response. I already done this with a normal response of just List and String without a problem but now I ran into massive problem. I don't have a clue how I can deserialise a Json that looks like this.
As requested
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
),
drawer: const NavigationDrawer(),
body: Column(
children: [
Center(
child: Text(templateName),
),
Center(
child: FutureBuilder<TemplateContentAndArgumentsObject>(
future: templateContent,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data?.TemplateContent ?? "null");
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
Api Call Code
Future<TemplateContentAndArgumentsObject> getTemplateContent(
String customerId, String templateName) async {
var url = Uri.parse(
'https://localhost:7167/api/v1/Templates/$customerId/$templateName');
var response = await http.get(url, headers: {
"Accept": "application/json",
"Access-Control-Allow-Origin": "*"
});
try {
print(response.body);
var sm = json.decode(response.body);
print(sm);
} catch (ex) {
print(ex);
}
if (response.statusCode == 200) {
TemplateContentAndArgumentsObject obj =
TemplateContentAndArgumentsObject.fromJson(json.decode(response.body));
print(obj.TemplateContent);
print(obj.TemplateArguments);
return obj;
} else {
print('Request failed with status: ${response.statusCode}');
}
return TemplateContentAndArgumentsObject(
TemplateContent: "", TemplateArguments: new List<String>.empty());
}
Class Object
import 'package:flutter/cupertino.dart';
class TemplateContentAndArgumentsObject {
String TemplateContent;
List<String> TemplateArguments;
TemplateContentAndArgumentsObject({
required this.TemplateContent,
required this.TemplateArguments,
});
factory TemplateContentAndArgumentsObject.fromJson(
Map<String, dynamic> json,
) =>
TemplateContentAndArgumentsObject(
TemplateContent: json["TemplateContent"] as String,
TemplateArguments: (json["TemplateArguments"] as List<String>),
);
}
Image of Json
Below is Sample Code for your problem. Please be aware that the code is just created based on your example where your list just contains String objects. In case your list contains more advanced objects, we should model them individually and put into a list. But for the strings you can do something like this:
class TemplateContentAndArgumentsObject {
String myStringContent;
List<String> myArrayContent;
TemplateContentAndArgumentsObject({
required this.myStringContent,
required this.myArrayContent,
});
factory TemplateContentAndArgumentsObject.fromJson(
Map<String, dynamic> json,
) =>
TemplateContentAndArgumentsObject(
myStringContent: json["myStringContent"] as String,
myArrayContent:
(json["myArrayContent"] as List<dynamic>).cast<String>(),
);
Map<String, Object> toJson() => {
"stringContnet": myStringContent,
"arrayCOntnet": myArrayContent,
};
}
I have changed the fromJson constructor into a factory constructor that just calls the class constructor. By doing so it removes the need for the class variables to be declared late.
Hey you can modify your build method, you need to check condition snapshot.hasData, for more detail see FutureBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
centerTitle: true,
backgroundColor: Colors.blueGrey,
foregroundColor: Colors.white,
),
drawer: const NavigationDrawer(),
body: Column(
children: [
Center(
child: Text(templateName),
),
Center(
child: FutureBuilder<TemplateContentAndArgumentsObject>(
future: templateContent,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data?.TemplateContent ?? "");
}else if (snapshot.hasError){
/// return error widget
return Container();
} else {
return CircularProgressIndicator();
}
},
),
),
],
),
);
//map to String
Map<String, dynamic> mapData = {
"username":name,
"email":email,
"phoneNumber":mobileNo,
"password":password ,
"refCode": inviteCode,
"countryCode":countryCode,
"country": "india"
};
json.encode(mapData);
// map to list
List values = List();
mapData.forEach((v) => values.add(v));
print(values);
I was working with rest api in flutter but I get error when I want create post options. I created task with post. I want solve this error. I have given the necessary code structures in the description. I hope I was able to explain clearly
Code Here:
My BaseClientClass
Future<dynamic> post(String baseUrl, String api, dynamic payloadObj) async {
var uri = Uri.parse(baseUrl + api);
String payload = json.encode(payloadObj);
try {
var response = await http.post(uri, body: payload, headers: {
'Content-Type': 'application/json'
}).timeout(const Duration(seconds: TIME_OUT_DURATION));
return _procosessResponse(response);
} on SocketException {
throw FetchDataException('No Internet connection', uri.toString());
} on TimeoutException {
throw ApiNotRespondingException(
'API not responded in time', uri.toString());
}
}
Controller class
Future postTodo(String task, bool active) async {
Map msg = {
"task": task,
"active": active
};
// String jsonS= json.encode(msg);
var response = await baseClient.post(
"http://192.168.1.114:5000", '/api/task', msg);
if (response.statusCode == 200 || response.statusCode == 201) {
var jsonData = json.decode(response.body);
if (jsonData['success']) { // eğer succes true ise
todolist.add(TodoModel.fromJson(jsonData['data']));
// var jsonData = json.encode(response);
print(msg);
}
}
add task Widget class
class AddTaskWidget extends StatelessWidget {
TextEditingController? task;
VoidCallback? onPress;
AddTaskWidget({this.task, this.onPress});
#override
Widget build(BuildContext context) {
return Container(
child: AlertDialog(
content: TextFormField(
controller: task,
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text("İptal")),
TextButton(onPressed: onPress, child: Text("Ekle"))
],
),
);
}
}
This call post function and task widget code
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => AddTaskWidget(
task: todoController.textTaskNameController,
onPress: () async{
await todoController.postTodo(
todoController.textTaskNameController.text, true);
Navigator.pop(context);
}),
),
icon: Icon(Icons.add))
Provide the API response payload once in JSON format
I'm trying to fetch and show data from the json file which contains a list of nested maps, as a beginner I'm facing lot of difficulties while coding it, please help me to fetch and show data from the json file. I don't know how to fetch data, I have to show the name, email, city, age etc in the App UI. I don't know what is the procedure to work this nested maps. Help me to make this code work.
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import 'package:task/model/model_class.dart';
class EmployeeListView extends StatefulWidget {
#override
_EmployeeListViewState createState() => _EmployeeListViewState();
}
class _EmployeeListViewState extends State<EmployeeListView> {
List<Results> _results = List<Results>();
#override
void initState() {
super.initState();
fetchResults.then((value){
setState(() {
_results.addAll(value);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Employee"),
backgroundColor: Colors.black,
),
body: ListView.builder(
itemCount: 10,
itemBuilder: (context, index){
return Card(
color: Colors.white,
shadowColor: Colors.black,
elevation: 4.5,
child: ListTile(
leading: CircleAvatar(
backgroundColor: Colors.transparent,
radius: 25.0,
backgroundImage: NetworkImage("url"),
),
title: Text("Name"),
subtitle: Text("Email"),
trailing: IconButton(icon: Icon(Icons.call), onPressed: (){}),
),
);
}),
);
}
Future<List<Results>> fetchResults() async{
var url = "https://randomuser.me/api?page=2&results=10&seed=99d7541361f1e116";
var response = await http.get(url);
if(response.statusCode == 200){
var resultsJson = json.decode(response.body);
for(var resultsJson in resultsJson){
_results.add(Results.fromJson(resultsJson));
return _results;
}
}
}
}
I can see you made a mistake somewhere in your for loop.
for(var resultsJson in resultsJson){
_results.add(Results.fromJson(resultsJson));
return _results;
You used the JsonList(resultsJson) to represent a Json object in the list.
Declare a new list too, and return outside the loop. Change this to;
List<Results> resultsList = [];
for(var result in resultsJson){
resultsList.add(Results.fromJson(result));
}
return resultsList;
Try that out and give feedback please.
Okay according to your code in init state. i don't recommend that you use setState in initState but rather inside didChangeDependences.
You have to decode the json data from the http request
From response its returning the [result] and [info]
I have use the result section and provided the details
Step 1:
class Results {
String gender;
String phone;
String nat;
DocumentReference reference;
Results(this.gender, this.phone, this.nat);
factory Results.fromSnapshot(DocumentSnapshot snapshot) {
Results newEmployee = Results.fromJson(snapshot.data());
newEmployee.reference = snapshot.reference;
return newEmployee;
}
factory Results.fromJson(Map<String, dynamic> json) =>
_resultsFromJson(json);
Map<String, dynamic> toJson() => _resultsToJson(this);
#override
String toString() => 'employeeName ${Results}';
}
Results _resultsFromJson(Map<String, dynamic> data) {
return Results(
data['gender'],
data['phone'],
data['nat'],
);
}
Map<String, dynamic> _resultsToJson(Results instance) {
return {
'gender': instance.gender,
'phone': instance.phone,
'nat': instance.nat,
};
}
Step 2:
List<Results> collectionData = [];
#override
void initState() {
super.initState();
fetchResults().then((value) {
setState(() {
collectionData.addAll(value);
});
});
}
Future<List<Results>> fetchResults() async {
List<Results> _results = [];
var url =
"https://randomuser.me/api?page=2&results=10&seed=99d7541361f1e116";
var response = await http.get(url);
if (response.statusCode == 200) {
var resultsJson = json.decode(response.body)['results'].cast<Map<String,dynamic>>();
await Future.forEach(resultsJson, (element) {
_results.add(Results.fromJson(element));
});
}
return Future.value(_results);
}
My http-get json data in flutter project it can't get the data, I've tested with postman and it's succeed, but in flutter it's didn't or because of me don't know how to put in. I've try different ways but none are succeed!
Can anyone help me?! If anyone need more info, I'll give it to you!
Flutter Debug Console: https://i.stack.imgur.com/IGZDP.png
Postman tested succeed: https://i.stack.imgur.com/khj9a.png
Postman tested failed: https://i.stack.imgur.com/Ee6tU.png
Future<Get> getdata(String id) async {
final username = 'test';
final password = '9876543';
final credentials = '$username:$password';
final stringToBase64 = utf8.fuse(base64);
final encodedCredentials = stringToBase64.encode(credentials);
final String apiUrl = "http://sv.com/api/values/";
Map<String, String> headers = {
HttpHeaders.contentTypeHeader: "application/json; charset=utf-8",
HttpHeaders.authorizationHeader: "Basic $encodedCredentials",
};
final response = await http.get(apiUrl,headers: headers);
if (response.statusCode == 200) {
final String responseString = response.body;
print(response.statusCode);
print(responseString);
return Get.fromJson(json.decode(response.body));
} else {
print(response.statusCode);
print(response.body);
}
}
Get class
class Get {
final String id;
Get({
this.id,
});
factory Get.fromJson(Map<String, dynamic> json) {
return Get(
id: json['id_machdien'].toString()
);
}
}
Main
class _MyHomePageState extends State<MyHomePage> {
Get _dataget;
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body:Center(
child: Column(
children: <Widget> [
RaisedButton(
onPressed: () async{
final Get dataget = await getdata(id);
setState(() {
_dataget = dataget;
});
},
child: Text('Mở',style: TextStyle(fontSize: 15)),
),
],
),
),
)
);
}
}
Let me correct you,
You can't send body with GET http request, so change the API to POST or send id_machdien as Query parameter.
Inside class Get You are trying to parse json['id_machdien'] but in your response there is no id_machdien, it must be one of the response's item (like : id, trang_thai_app).
I am very new to Flutter and now developing an app that lists restaurants.
I have an API endpoint that returns JSON data. Here it is: https://node.coredes.in/restaurants.
I have already done the layouts. But I don't know how to deal with JSON data. I already tried to do this from examples I got from net.
I want to know how can I use these fields - doc.name, doc.image_gallery[0], doc.location.locality, doc.friday.closing_at.hour, doc.friday.closing_at.minute, to make a list of cards?
Could anyone please help me out with a sample code?
Here is a sample code below, shows how to get the name of restaurants in your JSON file:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
void main() {
runApp(new MaterialApp(
home: new HomePage(),
));
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String url = "https://node.coredes.in/restaurants/";
List data;
/*onCreate*/
#override
void initState() {
// TODO: implement initState
super.initState();
getJSONData(); //method
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("my JSON app")),
body: new ListView.builder(
// itemCount: 1,
//itemCount: data==null ? 0 :data.length ,
itemCount: data == null ? 0 : data.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: new Text(data[index]['name'] ?? ''),
padding: EdgeInsets.all(20),
),
)
],
),
),
);
},
),
);
}
/*method*/ //RT is Future<String>
Future<String> getJSONData() async {
var response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
print(response.body);
debugPrint(response.body);
setState(() {
var convertDataToJson = json.decode(response.body);
data = convertDataToJson['doc'];
});
return "Success";
}
}
And you can play with data[index]['name']. For example if you want the city you can do something like that data[index]['location']['city'].
So the class model you said, is in order to make your code easy to understand and fast to be reused. Your JSON file have a complex embedded data on it, and it's a little difficult to access this data. So using a class model will be very helpful to get easily, whatever the value you want from it. Let me show you that with a concret example, to know when a restaurant will open and close, you need to go along with this hierarchy doc => opening_times => Sunday => opening_at and the same for the closing time. So let's translate it to our flutter code, if we want to get just the hour value, it will be like data[index]['opening_time']['Sunday']['opening_at']['hour'], and the same for minute value, same for all the rest. In this hierarchy we didn't took so long to arrive to the end, but you're free to imagine in a very big hierarchy, how it will be the situation. Let's return to our case, now let's take the same example and try to use, like you said, a class model instead of a hard coded JSON attributs. Let's write a model class for Restaurant.
class Restaurant {
String name;
String city;
String day;
bool isOpen;
int hourOpen;
int minuteOpen;
// ..
// ..
// The rest of your wanted attributs
Restaurant(
{this.name,
this.city,
this.day,
this.isOpen,
this.hourOpen,
this.minuteOpen});
}
After that, we will write a method, its main goal is to fill our restaurants variable.
Future<String> getRestaurants() async {
var response = await http
.get(Uri.encodeFull(url), headers: {"Accept": "application/json"});
var convertDataToJson = json.decode(response.body);
data = convertDataToJson['doc'];
List tempRestaurants = new List();
data.forEach((restaurant) => {
tempRestaurants.add(new Restaurant(
name: restaurant['name'],
city: restaurant['location']['city'],
day: 'Sunday',
isOpen: restaurant['opening_times']['Sunday']['is_open_today'],
hourOpen: restaurant['opening_times']['Sunday']['opening_at']
['hour'],
minuteOpen: restaurant['opening_times']['Sunday']['opening_at']
['minute'])),
});
setState(() {
this.restaurants = tempRestaurants;
});
}
Here is how we get data after writing the model.
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(title: Text("my JSON app")),
body: new ListView.builder(
itemCount: restaurants == null ? 0 : restaurants.length,
itemBuilder: (BuildContext context, int index) {
return new Container(
child: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
new Card(
child: new Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text("Name: ${restaurants[index].name}"),
Text("City: ${restaurants[index].city}"),
Text("Day: ${restaurants[index].day}"),
Text("IsOpen: ${restaurants[index].isOpen}"),
Text(
"Time: ${restaurants[index].hourOpen}:${restaurants[index].minuteOpen}"),
]),
padding: EdgeInsets.all(20),
),
)
],
),
),
);
},
),
);
}
PS: in this example, I just took Sunday value in every restaurant instance, so that I can show you how easily the access to data has become. But you can create a class model for it, let's say RestaurantDay, and have attributs like dayName, hourOpen, minuteOpen... And after that, you can link it with the logic where we get data.
Try this:
class DemoClass {
String name;
int Id;
DemoClass(this.name, this.Id);
static DemoClass fromJson(Map<String, dynamic> json) {
return DemoClass(json['name'] as String, json['Id'] as int);
}
Map<String, dynamic> toJson(DemoClass instance) =>
<String, dynamic>{
'name': instance.name,
'Id': instance.trackerId
};
static List<DemoClass> fromJsonListStr(String jsonStr){
var list = jsonDecode(jsonStr) as List;
List<DemoClass> myThing = list.map((e) => DemoClass.fromJson(e)).toList();
return myThing;
}
}
Use this to get a list of objects:
var resp = await http.get('http://apicall.com/array',
headers: {"Content-Type": "application/json"});
var ret = DemoClass.fromJsonListStr(resp.body);
return ret;
Use this to get a single object:
var resp = await http.get('http://apicall.com/array',
headers: {"Content-Type": "application/json"});
var data = jsonDecode(resp.body);
var obj = DemoClass.fromJson(data);