I'm facing the same issue since i start coding yesterday on my project which has a part of getting some of json data from a given api.
My api link is: http://alkadhum-col.edu.iq/wp-json/wp/v2/posts?_embed
I'm working on flutter SDK and i'm confused why it is not working with me!, my job is to get only link, title, and source_url objects but i cannot get it.
I tried the following code in flutter documentation
https://flutter.dev/docs/cookbook/networking/fetch-data
and after some of modification according to my needs didn't got any data.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Post> fetchPost() async {
final response =
await http.get('http://alkadhum-col.edu.iq/wp-json/wp/v2/posts/');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
class Post {
final int id;
String title;
String link;
Post({this.id, this.title, this.link});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'].toString(),
link: json['link'].toString()
);
}
}
void main() => runApp(MyApp(post: fetchPost()));
class MyApp extends StatelessWidget {
final Future<Post> post;
MyApp({Key key, this.post}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.link);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner
return CircularProgressIndicator();
},
),
),
),
);
}
}
I only got the meesage below:
Type List dynamic is not a subtype of type Map String, dynamic
Any help will be appreciated.
Thanks in advance.
Your JSON response is of type List<dynamic> but you are taking response in Map String, dynamic but you can do something like this
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:clickmeetplay/iam/user/postbean.dart';
import 'package:http/http.dart' as http;
class PostHome extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: PostScreen(),),);
}
}
class PostScreen extends StatefulWidget {
#override
_PostScreenState createState() => _PostScreenState();
}
class _PostScreenState extends State<PostScreen> {
List<Post> _postList =new List<Post>();
Future<List<Post> > fetchPost() async {
final response =
await http.get('http://alkadhum-col.edu.iq/wp-json/wp/v2/posts/');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
List<dynamic> values=new List<dynamic>();
values = json.decode(response.body);
if(values.length>0){
for(int i=0;i<values.length;i++){
if(values[i]!=null){
Map<String,dynamic> map=values[i];
_postList .add(Post.fromJson(map));
debugPrint('Id-------${map['id']}');
}
}
}
return _postList;
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
#override
Widget build(BuildContext context) {
return Container();
}
#override
void initState() {
fetchPost();
}
}
Bean class
class Post {
final int id;
String title;
String link;
Post({this.id, this.title, this.link});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'].toString(),
link: json['link'].toString()
);
}
}
You need to change your class. you need to create your class structure as per the JSON response.
class Post {
int id;
String title;
String link;
Post({this.id, this.title, this.link});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: title = json['title'] != null ? new Title.fromJson(json['title']) : null;
link: json['link'].toString()
);
}
}
class Title {
String rendered;
Title({this.rendered});
Title.fromJson(Map<String, dynamic> json) {
rendered = json['rendered'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['rendered'] = this.rendered;
return data;
}
}
Then, in your API method. As the response, you are getting is a JSON array. So, take that in a list and cast the response into your JSON class.
Future<List<Post>> fetchPost() async {
final response =
await http.get('http://alkadhum-col.edu.iq/wp-json/wp/v2/posts/');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
var lst = response.body as List;
return lst.map((d) => Post.fromJson(d)).toList();
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
You can always use tools like https://javiercbk.github.io/json_to_dart/ to create Dart classes from complex JSON. Which saves a lot of time.
Try below code:
factory Post.fromMap(Map<String, dynamic> json) {
return Post(
id: json['id'],
title: json['title'].cast<String>(),
link: json['link'].cast<String>()
);
}
**Create Api Class**
class ApiUtils {
static String baseUrl = "http://example/";
}
**Create Model**
class EventModel {
String title;
EventModel({this.title});
EventModel.fromJson(Map<String, dynamic> json) {
title = json['Title'] ?? "";
}
}
**Create Service**
import 'package:http/http.dart' as http;
import 'package:NoticeModel.dart';
import 'dart:convert';
class NoticeService {
bool error = false;
bool loading = true;
var notice;
bool noticeDetailserror = false;
bool noticeetailsloading = true;
Future<void> getNotice(dynamic input) async {
try {
noticeDetailserror = false;
http.Response response = await http.post(ApiUtils.noticeApi,
body: input, headers: {'Content-type': 'application/json'});
Map data = jsonDecode(response.body);
if (data["Status"] == true) {
notice = data["Data"];
notice = notice.map((_data) {
return new NoticeModel.fromJson(_data);
}).toList();
print(notice);
noticeetailsloading = false;
} else {
throw data;
}
} catch (e) {
print(e);
noticeetailsloading = false;
noticeDetailserror = true;
}
}
}
**Main Page**
var body =
json.encode({"IsActive": true, "IsDelete": false, "CompanyId": 18});
List<dynamic> data;
var count = 0;
bool loading = true;
bool error = false;
void getNoticeDetails() async {
setState(() {
error = false;
loading = true;
});
// SharedPreferences prefs = await SharedPreferences.getInstance();
NoticeService instance = NoticeService();
await instance.getNotice(body);
data = instance.notice;
if (instance.noticeDetailserror == false) {
setState(() {
count = data.length;
loading = false;
});
} else {
setState(() {
loading = false;
error = true;
});
Toast.show("Error Getting Data", context,
duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
}
}
#override
void initState() {
super.initState();
getNoticeDetails();
}
body: loading == true
? LinearProgressIndicator()
: error == true
? RetryWidget(
onRetry: getNoticeDetails,
)
: SafeArea(
SizedBox(
width: 270,
child: Text(
data[index].title ?? "",
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 18,
color: Colors.grey,
fontWeight: FontWeight.bold,
),
),
),
)
Related
I have a collection named "ProductDatabase" that contains documents for all available products.
I aim to make an authenticated request to my server, which will return a snapshot of all the documents.
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:redue/pages/menu.dart';
import '../main.dart';
import 'listProducts.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:favorite_button/favorite_button.dart';
import 'map.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class listPage1 extends StatefulWidget {
const listPage1({super.key});
#override
State<listPage1> createState() => listPage3();
}
class listPage3 extends State<listPage1> {
late Future<Album> futureAlbum;
Widget _buildListItem(BuildContext context, DocumentSnapshot document) {
return Container(
child: FutureBuilder<List<Album>>(
future: fetchAlbum(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].productName),
);
});
}
else {
return const CircularProgressIndicator();
}
}));
}
}
Future<List<Album>> fetchAlbum() async {
String username = 'xxxxxx';
String password = 'xxxxxxxx';
String basicAuth =
'Basic ' + base64Encode(utf8.encode('$username:$password'));
print(basicAuth);
final r = await http.get(
Uri.parse(
'http://0.0.0.0:5000/apiv1/read?collection=ProductDatabase&document=all'),
headers: <String, String>{'authorization': basicAuth});
print(r.body);
if (r.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return json.decode(r.body).cast<Album>();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
class Album {
final String brand;
final int quantity;
final String SBD;
final String storeName;
final String price;
final String productName;
final String ID;
const Album({
required this.brand,
required this.quantity,
required this.SBD,
required this.storeName,
required this.price,
required this.productName,
required this.ID,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
brand: json["Data"]['Brand'],
quantity: json["Data"]['quantity'],
SBD: json["Data"]['SBD'],
storeName: json["Data"]['storeName'],
price: json["Data"]['price'],
productName: json["Data"]['productName'],
ID: json['ID'],
);
}
}
Currently, I'm running the server locally and when I run the above code, it throws the "failed to load album" exception, even though my command prompt shows that a request was made and the status code was 200.
You need to parse the response as a json, not casting as Album.
So, something like:
if (r.statusCode == 200) {
var list = json.decode(r.body) as List;
return list.map((e) => Album.fromJson(e)).toList();
} else {
...
By providing sample response, you can test it like:
void main() {
var response = '[]';
var list = json.decode(response) as List;
var albums = list.map((e) => Album.fromJson(e)).toList();
print(albums);
}
In Flutter, I want to pass a parameter ('1') and fetch the album title where the id = 1; the API is written in Java Springboot and the backend is MS SQL Database. Please help me to learn how to pass the parameter in my query in http.get or post method.
Currently I get an Exception error and the http response code is 400 or 405
Here's my code
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.xxx.com/albums/');
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Album> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Fetch Data Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: Text('Fetch Data Example'),
),
body: Center(
child: FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data.title);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
);
}
}
You should use the URL to set parameters :
Future<Album> fetchAlbum(number id) async {
final response =
await http.get('https://jsonplaceholder.xxx.com/albums/$id');
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
So you can pass the id to the method that will call your API.
You can search for stuff like REST API and GET parameters : REST API Best practices: Where to put parameters?
If you want to send it in post format, you can send it like this
When body is form data :
Future<Album> fetchAlbum() async {
final response = await http.post('https://jsonplaceholder.xxx.com/albums/',
body: <String, String> {
'id': '1',
},
);
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
When body is json :
Future<Album> fetchAlbum() async {
final response = await http.post(
'https://jsonplaceholder.xxx.com/albums/',
body: jsonEncode(
{
'id': '1',
},
),
headers: {'Content-Type': "application/json"},
);
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
Since the http/http.dart package has been added to your code, I recommend reading the following documentation.
https://pub.dev/packages/http
And the http error codes 400 and 405 are
400: Bad Request
405: Method Not Allowed
I am new to flutter and trying to fetch API data into a list. My goal is to later input that information into a ListView.
I am familiar with doing api calls but now I'm triying to get information from a json array. Previously I made a model class to retrieve the objects I need to continue with my process, but I've reached a dead end since I can't seem to figure out how to extract the objects from an array properly.
I am using multiple files. "API Services", "readData Model", and the actual screen where the data will be shown.
Everything else is fine, it's just that I am unable to actually save the data to a list.
First I will show you how my code is set up and the JSON response data I am trying to save:
JSON Response Body:
The example I am showing only has one chunk of image data, but we need an array for it since it should be displaying every chunk in a list.
{"status":200,
"content":[{"image_id":"151",
"image_url":"https:\\\/imageurl.jpg",
"returned_value":"14.0",
"alarm":"false",
"account":"test#email.com",
"create_at":"2020-11-17 07:13:42",
"location":"NY"
}]
}
API POST function:
Future<ReadResponseModel> readData(
ReadRequestModel requestData) async {
final response = await http.post("$url/read",
body: requestData.toJson() ,
headers: {
"Client-Service": "frontend-client",
"Auth-Key": "simplerestapi",
"Content-Type":"application/x-www-form-urlencoded",
"Authorization": token,
"User-ID": userId,
});
print(response.body);
if (response.statusCode == 200 || response.statusCode == 400) {
dynamic resBody = json.decode(response.body);
return ReadResponseModel.fromJson(resBody);
} else {
throw Exception('Failed to load data!');
}
}
readResponseModel Class:
I have tried two methods to process this information but have failed at both of them.
This is Method 1:
This one will give me the following error: [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
Originally set as final String for all values, but since I was getting this error I've been trying to make sure each one is the correct type (ex. retVal as int or alarm as bool). Still no luck so far.
class ReadResponseModel {
final dynamic imageID;
final dynamic imgUrl;
final dynamic retVal;
final bool alarm;
final dynamic account;
final dynamic createAt;
final dynamic location;
ReadResponseModel({this.imageID, this.imgUrl, this.retVal, this.alarm,
this.account, this.createAt, this.location,});
factory ReadResponseModel.fromJson(Map<String , dynamic> json) {
return ReadResponseModel(
imageID: json['content']['image_id'] as String ,
imgUrl: json['content']['image_url'] as String ,
retVal: json['content']["returned_value"] as String,
alarm: json['content']['alarm'] as bool ,
account: json['content']['account'] as String,
createAt: json['content']['create_at'] as String,
location: json['content']['location'] as String,
);
}
}
Method 2:
This one will give me the following error:
[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type '(dynamic) => Content' is not a subtype of type '(dynamic) => String' of 'f' -- I dont even know where that f came from.
class ReadResponseModel {
List<String> content;
ReadResponseModel({this.content});
factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
return ReadResponseModel(
content: json['content'] != null
? json['content']
.map<String>((json) => Content.fromJson(json))
.toList()
: null,
);
}
}
class Content {
final String imageID;
final String imgUrl;
final String retVal;
final String alarm;
final String account;
final String createAt;
final String location;
final String error;
Content({this.imageID,
this.imgUrl,
this.retVal,
this.alarm,
this.account,
this.createAt,
this.location,
this.error});
factory Content.fromJson(Map<String, dynamic> json) =>
Content(
imageID: json['image_id'],
imgUrl: json['s3_url'],
retVal: json['predict'],
alarm: json['alarm'],
account: json['account'],
createAt: json['create_at'],
location: json['location'],
);
}
The following code is just what I'm doing to connect all this to my widget:
APIService readService = new APIService();
readRequestModel.companyID = "123";
readService.readData(readRequestModel).then((value) async {
if (value != null) {
setState(() {
isApiCallProcess = false;
});
///Trying to call the fetched data and show it in the console:
print("Account: ${value.account}");
print("Returned Value: ${value.retVal}");
print("Image ID: ${value.imgUrl}");
}
I'm not entirely sure why I am getting these errors, and for method 2 I don't even know where that "f" came from. If anyone could shed some light on the subject it would be greatly appreciated.
You can copy paste run full code below
Step 1: Use List<Content> content; not List<String>
Step 2: Use List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
Step 3: In sample JSON's image_url not equal model's s3_url, you need to modify to correct one
code snippet
class ReadResponseModel {
List<Content> content;
ReadResponseModel({this.content});
factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
return ReadResponseModel(
content: json['content'] != null
? List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
: null,
);
}
}
...
dynamic resBody = json.decode(jsonString);
ReadResponseModel model = ReadResponseModel.fromJson(resBody);
print(model.content[0].account);
output
I/flutter ( 4426): test#email.com
full code
import 'package:flutter/material.dart';
import 'dart:convert';
class ReadResponseModel {
List<Content> content;
ReadResponseModel({this.content});
factory ReadResponseModel.fromJson(Map<String, dynamic> json) {
return ReadResponseModel(
content: json['content'] != null
? List<Content>.from(json["content"].map((x) => Content.fromJson(x)))
: null,
);
}
}
class Content {
final String imageID;
final String imgUrl;
final String retVal;
final String alarm;
final String account;
final String createAt;
final String location;
final String error;
Content(
{this.imageID,
this.imgUrl,
this.retVal,
this.alarm,
this.account,
this.createAt,
this.location,
this.error});
factory Content.fromJson(Map<String, dynamic> json) => Content(
imageID: json['image_id'],
imgUrl: json['s3_url'],
retVal: json['predict'],
alarm: json['alarm'],
account: json['account'],
createAt: json['create_at'],
location: json['location'],
);
}
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
String jsonString = '''
{"status":200,
"content":[{"image_id":"151",
"image_url":"https:\\\/imageurl.jpg",
"returned_value":"14.0",
"alarm":"false",
"account":"test#email.com",
"create_at":"2020-11-17 07:13:42",
"location":"NY"
}]
}
''';
dynamic resBody = json.decode(jsonString);
ReadResponseModel model = ReadResponseModel.fromJson(resBody);
print(model.content[0].account);
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
From the above mentioned code I have created a sample example for you, basically you are messing up with the mode class.Take a look at the example below.
Json you provided:
{"status":200,
"content":[{"image_id":"151",
"image_url":"https:\\\/imageurl.jpg",
"returned_value":"14.0",
"alarm":"false",
"account":"test#email.com",
"create_at":"2020-11-17 07:13:42",
"location":"NY"
}]
}
Based on the json the model class below :
// To parse this JSON data, do
//
// final readResponseModel = readResponseModelFromJson(jsonString);
import 'dart:convert';
ReadResponseModel readResponseModelFromJson(String str) => ReadResponseModel.fromJson(json.decode(str));
String readResponseModelToJson(ReadResponseModel data) => json.encode(data.toJson());
class ReadResponseModel {
ReadResponseModel({
this.status,
this.content,
});
int status;
List<Content> content;
factory ReadResponseModel.fromJson(Map<String, dynamic> json) => ReadResponseModel(
status: json["status"],
content: List<Content>.from(json["content"].map((x) => Content.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"status": status,
"content": List<dynamic>.from(content.map((x) => x.toJson())),
};
}
class Content {
Content({
this.imageId,
this.imageUrl,
this.returnedValue,
this.alarm,
this.account,
this.createAt,
this.location,
});
String imageId;
String imageUrl;
String returnedValue;
String alarm;
String account;
DateTime createAt;
String location;
factory Content.fromJson(Map<String, dynamic> json) => Content(
imageId: json["image_id"],
imageUrl: json["image_url"],
returnedValue: json["returned_value"],
alarm: json["alarm"],
account: json["account"],
createAt: DateTime.parse(json["create_at"]),
location: json["location"],
);
Map<String, dynamic> toJson() => {
"image_id": imageId,
"image_url": imageUrl,
"returned_value": returnedValue,
"alarm": alarm,
"account": account,
"create_at": createAt.toIso8601String(),
"location": location,
};
}
And them main class for fetching the data and showing it in the listview.
import 'package:flutter/material.dart';
import 'package:json_parsing_example/model2.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleApp(),
debugShowCheckedModeBanner: false,
);
}
}
class SampleApp extends StatefulWidget {
#override
_SampleAppState createState() => _SampleAppState();
}
class _SampleAppState extends State<SampleApp> {
bool _isLoading = false;
List<Content> list = List();
fetchData() async {
setState(() {
_isLoading = true;
});
String data =
await DefaultAssetBundle.of(context).loadString("json/parse.json");
// This is the above where you get the remote data
// Like var response = await get or post
final readResponseModel = readResponseModelFromJson(data);
list = readResponseModel.content;
setState(() {
_isLoading = false;
});
}
#override
void initState() {
super.initState();
fetchData();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Your heading'),
),
body: Container(
child: _isLoading
? Center(child: CircularProgressIndicator())
: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return Card(
child: Column(
children: <Widget>[
Text('${list[index].account}'),
Text('${list[index].location}')
],
),
);
})
],
)));
}
}
Let me know if it works
Can you try setting returned_value and image_id as int instead of string and give it a try ?
I am using an API,to fetch data and display it in a list view after some constraints.I am fetching the data from the API ,it is printing as well and getting stored as well.But still I am getting Unhandled Exception: DatabaseException(datatype mismatch),though I checked the order in which I have put them down in correct.I am really fed up with this JSON parsing.It is a real pain actually speaking.
Below is the list of dart files I have used in my flutter project:
This is my main file,where I will be displaying the data in a list view:
import 'package:flutter/material.dart';
import 'services.dart';
import 'db_provider.dart';
void main() => runApp(Main());
class Main extends StatefulWidget {
#override
_MainState createState() => _MainState();
}
class _MainState extends State<Main> {
var isLoading = false;
_loadFromApi() async {
setState(() {
isLoading = true;
});
var apiProvider = Services();
await apiProvider.getAllCustomers();
// wait for 2 seconds to simulate loading of data
await Future.delayed(const Duration(seconds: 2));
setState(() {
isLoading = false;
});
}
Widget _buildEmployeeListView() {
return FutureBuilder(
future: DBProvider.db.getAllCustomers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.separated(
separatorBuilder: (context, index) =>
Divider(
color: Colors.black12,
),
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Text(
"${index + 1}",
style: TextStyle(fontSize: 20.0),
),
title: Text(
"Name: ${snapshot.data[index].name} "),
subtitle: Text('status: ${snapshot.data[index].status}'),
);
},
);
}
},
);
}
#override
void initState() {
super.initState();
_loadFromApi();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Customer details'),
),
body: _buildEmployeeListView(),
),
);
}
}
Below is my JSON data:
{
"list":[
{
"id":"MnJJA0dbuw",
"name":"Anand Sharma",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Anand.jpeg",
"gender":"m",
"age":46,
"date":"23/11/2019",
"status":"onboarded"
},
{
"id":"MnZmmmbuw",
"name":"Malashri Lal",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Vishesh.jpg",
"gender":"f",
"age":70,
"date":"01/10/2019",
"status":"active"
},
{
"id":"MnZy10dpsq",
"name":"Suhasini Haidar",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Suhasini.jpg",
"gender":"f",
"age":30,
"date":"23/03/2019",
"status":"left"
},
{
"id":"HOnmFt5jA",
"name":"Vishesh Mahajan",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Vishesh.jpg",
"gender":"m",
"age":34,
"date":"05/05/2019",
"status":"active"
},
{
"id":"MnZy10dxyz",
"name":"Anand Neelakantan",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Anand.jpeg",
"gender":"m",
"age":46,
"date":"03/10/2019",
"status":"left"
},
{
"id":"MnZy1JJbuw",
"name":"Malashri",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Malashri.jpg",
"gender":"f",
"age":65,
"date":"04/01/2019",
"status":"active"
},
{
"id":"JKAS10dbuw",
"name":"Meenakshi Madhavan",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Meenakshi.jpg",
"gender":"f",
"age":35,
"date":"01/03/2020",
"status":"left"
},
{
"id":"sOw8c6BOug",
"name":"Fintan",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Fintan.jpg",
"gender":"m",
"age":55,
"date":"09/12/2019",
"status":"onboarded"
},
{
"id":"MnZy10dlll",
"name":"Jaishree Periwal",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Jaishree.jpg",
"gender":"f",
"age":50,
"date":"01/02/2020",
"status":"active"
},
{
"id":"KbN3VELflA",
"name":"Anukriti Upadhyay",
"img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Anukriti.jpg",
"gender":"f",
"age":30,
"date":"31/01/2020",
"status":"onboarded"
}
]
}
Below is the PODO file I have created:
// To parse this JSON data, do
//
// final customerInfo = customerInfoFromJson(jsonString);
import 'dart:convert';
CustomerInfo customerInfoFromJson(String str) => CustomerInfo.fromJson(json.decode(str));
String customerInfoToJson(CustomerInfo data) => json.encode(data.toJson());
class CustomerInfo {
CustomerInfo({
this.list,
});
List<ListElement> list;
factory CustomerInfo.fromJson(Map<String, dynamic> json) => CustomerInfo(
list: List<ListElement>.from(json["list"].map((x) => ListElement.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"list": List<dynamic>.from(list.map((x) => x.toJson())),
};
}
class ListElement {
ListElement({
this.id,
this.name,
this.img,
this.gender,
this.age,
this.date,
this.status,
});
String id;
String name;
String img;
Gender gender;
int age;
String date;
Status status;
factory ListElement.fromJson(Map<String, dynamic> json) => ListElement(
id: json["id"],
name: json["name"],
img: json["img"],
gender: genderValues.map[json["gender"]],
age: json["age"],
date: json["date"],
status: statusValues.map[json["status"]],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"img": img,
"gender": genderValues.reverse[gender],
"age": age,
"date": date,
"status": statusValues.reverse[status],
};
}
enum Gender { M, F }
final genderValues = EnumValues({
"f": Gender.F,
"m": Gender.M
});
enum Status { ONBOARDED, ACTIVE, LEFT }
final statusValues = EnumValues({
"active": Status.ACTIVE,
"left": Status.LEFT,
"onboarded": Status.ONBOARDED
});
class EnumValues<T> {
Map<String, T> map;
Map<T, String> reverseMap;
EnumValues(this.map);
Map<T, String> get reverse {
if (reverseMap == null) {
reverseMap = map.map((k, v) => new MapEntry(v, k));
}
return reverseMap;
}
}
Below is the db provider I have created to store the data.I need to still write the SQL command to properly get the data in the required sorting order.For the time being it is Select *
import 'dart:io';
import 'podo_file.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
//9-22 creating a Database object and instantiating it if not initialized.
class DBProvider {
static Database _database;
static final DBProvider db = DBProvider._();
DBProvider._();
Future<Database> get database async {
// If database exists, return database
if (_database != null) return _database;
// If database don't exists, create one
_database = await initDB();
return _database;
}
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
final path = join(documentsDirectory.path, 'Customer_Info.db');
return await openDatabase(path, version: 1, onOpen: (db) {},
onCreate: (Database db, int version) async {
await db.execute('CREATE TABLE CUSTOMER_DATA('
'id TEXT PRIMARY KEY,'
'name TEXT,'
'img TEXT,'
'gender TEXT'
'age INTEGER,'
'date TEXT,'
'status TEXT,'
')');
});
}
// Insert employee on database
createCustomer(ListElement newCustomer) async {
await deleteAllCustomers();
final db = await database;
final res = await db.insert('CUSTOMER_DATA', newCustomer.toJson());
return res;
}
// Delete all employees
Future<int> deleteAllCustomers() async {
final db = await database;
final res = await db.rawDelete('DELETE FROM CUSTOMER_DATA');
return res;
}
Future<List<ListElement>> getAllCustomers() async {
final db = await database;
final res = await db.rawQuery("SELECT * FROM CUSTOMER_DATA");
List<ListElement> list =
res.isNotEmpty ? res.map((c) => CustomerInfo.fromJson(c)).toList() : [];
return list;
}
}
Below is the service file I have created which will get the data:
import 'dart:convert';
import 'package:flutter/animation.dart';
import 'package:flutterappincablet/podo_file.dart';
import 'package:http/http.dart' as http;
import 'db_provider.dart';
class Services {
Future<List<ListElement>> getAllCustomers() async {
var url = "https://5w05g4ddb1.execute-api.ap-south-1.amazonaws.com/dev/profile/listAll";
final response = await http.get(url);
Map<String, dynamic> map = json.decode(response.body);
print('nnnnnnnnnn');
print(map);
List<dynamic> data = map["list"];
print('xxxxxxxxxxxxxxxxx');
print(data);
return (data as List).map((c) {
print('Inserting $c');
DBProvider.db.createCustomer(ListElement.fromJson(c));
}).toList();
}
}
Below is the screenshot of the error:
So, I am pretty new to async programming in flutter, I came across a json fine which looks like this:
[{
"Bus": ["a", "b", "c"],
"Stops": ["1", "2", "3", "4"]
}]
So, I made a class:
class StopInfo {
final List Bus;
final List Stop;
StopInfo({this.Bus, this.Stop});
factory StopInfo.fromJson(Map<String, dynamic> json) {
return StopInfo(
busNames: json["Bus"],
busStops: json["Stops"]
);
}
}
Now, if I am fetching Data like this:
Future<StopInfo> fetchStopData() async {
var response = await http.get(url);
print(response.body);
StopInfo _stopInfo = StopInfo();
if (response.statusCode == 200) {
/*
What do I do here
*/
} else {
throw Exception('Failed to get data');
}
}
I am confused what to do here, Should I change something else?
Thanks in advance.
here is a simple example which shows how you can extract and use JSON data:
import "package:flutter/material.dart";
import "dart:convert";
import "package:http/http.dart" as http;
class JsonMultyParse extends StatefulWidget {
#override
_JsonMultyParseState createState() => _JsonMultyParseState();
}
class _JsonMultyParseState extends State<JsonMultyParse> {
final String apiURL = 'https://jsonplaceholder.typicode.com/users';
List<dynamic> users = [];
void fetchJSONData() async {
var jsonResponse = await http.get(apiURL);
if (jsonResponse.statusCode == 200) {
final jsonItems =
json.decode(jsonResponse.body).cast<Map<String, dynamic>>();
print(jsonItems[0]["address"]["street"]);
setState(() {
users = jsonItems;
});
for (dynamic user in users) {
print(user["address"]["street"]);
}
} else {
throw Exception('Failed to load data from internet');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Container(
child: Column(
children: <Widget>[
SizedBox(height: 20),
RaisedButton(
onPressed: fetchJSONData,
child: Text("Fetch Data"),
),
Expanded(
child: ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
return Card(
child: Container(
height: 50,
padding: const EdgeInsets.all(8.0),
child: Text(
users[index]["address"]["street"],
),
),
);
}),
)
],
),
),
));
}
}
Output:
import 'dart:convert';
Future<List<StopInfo>> fetchStopData() async {
var response = await http.get(url);
print(response.body);
StopInfo _stopInfo = StopInfo();
if (response.statusCode == 200) {
var body = jsonDecode(response.body);
if(body is List) //check if it's a List, as per your example I suppose it will always be a list
return List<StopInfo>.from(body.map(map) => StopInfo.fromJson(map));
else if(body is Map) //check if it's a Map
return [StopInfo.fromJson(body)];
} else {
throw Exception('Failed to get data');
}
}
jsonDecode is part of dart:convert and decode a String to a dynamic value (can be a List or a Map) according to the form of the JSON data it decodes, after that you just convert it to List of StopInfo (if thats what you were expecting from the JSON
Add the following Function in your StopInfo class, which will convert jsonbody into List.
List<StopInfo> stopInfoResponseFromJson(String str) {
final jsonData = json.decode(str);
return new List<StopInfo>.from(jsonData.map((x) => StopInfo.fromJson(x)));
}
Use the above function in your fetchStopData function, like following.
String response = jsonEncode(StopInfo.body);
List<StopInfo> stopInfoResponse = stopInfoResponseFromJson(response);
Try this,
Future<StopInfo> fetchStopData() async {
var response = await http.get(url);
print(response.body);
StopInfo _stopInfo = StopInfo();
if (response.statusCode == 200) {
return StopInfo.fromJson(json.decode(response.body.toString())[0]);
} else {
throw Exception('Failed to get data');
}
}
also modify the StopInfo
factory StopInfo.fromJson(Map<String, dynamic> json) {
List<String> buses=List();
List<String> stops=List();
json["Bus"].forEach((bus)=>buses.add(bus));
json["Stops"].forEach((stop)=>stops.add(stop));
return StopInfo(
busNames: buses,
busStops: stops
);
}
Can you try the following. I generated the class using https://app.quicktype.io/
Change your StopInfo class as
// To parse this JSON data, do
//
// final stopInfo = stopInfoFromJson(jsonString);
import 'dart:convert';
class StopInfo {
StopInfo({
this.bus,
this.stops,
});
List<String> bus;
List<String> stops;
factory StopInfo.fromJson(Map<String, dynamic> json) => StopInfo(
bus: List<String>.from(json["Bus"].map((x) => x)),
stops: List<String>.from(json["Stops"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"Bus": List<dynamic>.from(bus.map((x) => x)),
"Stops": List<dynamic>.from(stops.map((x) => x)),
};
}
and to convert back and forth use these functions
List<StopInfo> stopInfoFromJson(String str) => List<StopInfo>.from(json.decode(str).map((x) => StopInfo.fromJson(x)));
String stopInfoToJson(List<StopInfo> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
You probably need to use the first one and use the list to create the display widgets. The following is a minimal example
class _MyHomePageState extends State<MyHomePage> {
String busStops = '''
[{
"Bus": ["a", "b", "c"],
"Stops": ["1", "2", "3", "4"]
}]
''';
List<StopInfo> allStops = [];
List<Widget> cardsList = [];
#override
void initState() {
super.initState();
loadPrelimData();
}
void loadPrelimData(){
setState(() {
allStops.clear();
allStops = stopInfoFromJson(busStops);
fillColumnChildren();
});
}
void fillColumnChildren(){
cardsList.clear();
allStops.forEach((stopInfos) {
String stops = "";
stopInfos.stops.forEach((element) { stops += element + ", "; });
stopInfos.bus.forEach((element) {
cardsList.add(
//the widget that will be displayed
Card(
child: ListTile(title: Text("Bus:" + element),
subtitle: Text("Stops:" + stops),
),
)
);
});
});
}
List<StopInfo> stopInfoFromJson(String str) => List<StopInfo>.from(json.decode(str).map((x) => StopInfo.fromJson(x)));
String stopInfoToJson(List<StopInfo> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Bus stops"),),
body: Center(
child: Column(children: cardsList)
));
}
}
I get the following
You model
class StopInfo {
final List<String> busList;
final List<String> stopList;
StopInfo({
this.busList,
this.stopList,
});
StopInfo copyWith({
List<String> busList,
List<String> stopList,
}) {
return StopInfo(
busList: busList ?? this.busList,
stopList: stopList ?? this.stopList,
);
}
Map<String, dynamic> toMap() {
return {
'busList': busList,
'stopList': stopList,
};
}
static StopInfo fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return StopInfo(
busList: List<String>.from(map['busList']),
stopList: List<String>.from(map['stopList']),
);
}
String toJson() => json.encode(toMap());
static StopInfo fromJson(String source) => fromMap(json.decode(source));
#override
String toString() => 'StopInfo(busList: $busList, stopList: $stopList)';
#override
bool operator ==(Object o) {
if (identical(this, o)) return true;
return o is StopInfo &&
listEquals(o.busList, busList) &&
listEquals(o.stopList, stopList);
}
#override
int get hashCode => busList.hashCode ^ stopList.hashCode;
}
Your Service
Future<StopInfo> fetchStopData() async {
var response = await http.get(url);
print(response.body);
if (response.statusCode == 200) {
return StopInfo.fromJson(response.body);
} else {
throw Exception('Failed to get data');
}
}