I am creating a Books app in Flutter and I have an Exception in api.dart file which is working fine according to the code. But I am unable to fetch the books and their properties while using the google Books API. I can't figure out why the link is not working. I hope you can help me out. The api key is hidden but I've copied it right. This is the code:
api.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'books.dart';
import 'package:books_app/config.dart';
class BooksApi {
Future<List<Book>> fetchBooks() async {
final response = await http.get(Uri.parse(
'https://www.googleapis.com/books/v1/volumes?key=$apiKey'));
if (response.statusCode == 200) {
// If the call to the API was successful, parse the JSON
List<dynamic> jsonResponse = jsonDecode(response.body)['items'];
return (jsonResponse.map((book) => Book.fromJson(book)).toList());
} else {
throw Exception('Failed to load books');
}
}
}
books.dart
class Book {
final String title;
final String author;
final String description;
final DateTime datePublished;
final String isbn;
final String imageUrl;
Book(
{required this.title,
required this.author,
required this.description,
required this.datePublished,
required this.isbn,
required this.imageUrl});
factory Book.fromJson(Map<String, dynamic> json) {
return Book(
title: json['volumeInfo']['title'],
author: json['volumeInfo']['author'][0],
description: json['volumeInfo']['description'],
datePublished: json['volumeInfo']['datePublished'],
isbn: json['volumeInfo']['ISBN'],
imageUrl: json['volumeInfo']['imageLinks']['thumbnail']);
}
}
main.dart
import 'package:books_app/api.dart';
import 'package:flutter/material.dart';
import 'books.dart';
final booksAPI = BooksApi();
void main() {
runApp(const MyApp());
}
class DisplayBooks extends StatefulWidget {
const DisplayBooks({super.key});
#override
State<DisplayBooks> createState() => _DisplayBooksState();
}
class _DisplayBooksState extends State<DisplayBooks> {
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Book>>(
future: booksAPI.fetchBooks(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].title),
subtitle: Text(snapshot.data![index].author),
leading: Image.network(snapshot.data![index].imageUrl),
);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const CircularProgressIndicator();
},
);
}
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
home: const Scaffold(body: DisplayBooks()),
debugShowCheckedModeBanner: false,
title: 'BooksApp',
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: Colors.black),
);
}
}
This is the response code:
response code
Your are missing the query in the api call for volumes. Because just calling volumes won't return anything. And your status, 400 is a "Bad Request" and that indicates that the server could not understand the requested call.
?q=query&
https://www.googleapis.com/books/v1/volumes?q=$query&key=$apiKey
So you need to pass in a query for to fetch all the books you want.
Google Books API just won't let you return all of their billions of books, you need to pass a query and narrow it down to languages, genre, etc.
Read more here, for what queries you can do
Google Books api query documentation
Then You need to pass the books into your model and make sure you're not missing any field or if a field can be missing make sure that field is then optional. And your DateTime should be string because the json you receive doesn't contain any DateTime objects so you need to convert that response into datetime in your code.
Related
I am using http and trying to display the data and an error shows to me in the screen instead of the data I defined all the variables with the null safety he should print the data inside the title not the error
this is the json class #link#
this is the code
// ignore_for_file: unused_local_variable, avoid_print
import 'dart:convert';
import 'package:appii/models/post.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
late Future<Post>
postdata; // this one is for saving the data in variable that i can use it anywhere
//we did the init state to allow the compile to print the data
#override
void initState() {
super.initState();
postdata = getPostById();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Networking http lesson 1'),
),
body: Center(
child: FutureBuilder<Post>(
future: postdata, // the data source
// in the builder i am going to desigm the place where the data will be in and it take #1- Context 2- snapshot#
builder: (context, snapshot) {
// this one is to see if there is data or eror
if (snapshot.hasData) {
return Text(snapshot.data!.title);
} else if (snapshot.hasError) {
return Text('error is ${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
);
}
// this is the fuction that by it iam requesting to use the http package #http.# this is an object from the package i called containes all the methods inside this package
Future<Post> getPostById() async {
http.Response futurepost = await http.get(
Uri.parse(
'https://jsonplaceholder.typicode.com/posts/1'), // here in http.get or http.post first yu have to put ur url in the shape of #Uri.parse# To improve compile-time type safety
);
//this method #if# like .then but here i making sure that tha api and the link is working
if (futurepost.statusCode == 200) {
// success
print(futurepost.body);
return Post.fromjson(json.decode(futurepost
.body)); // in thie retuen iam taking the data and convert it form data with double qoutations to json
} else {
throw Exception(
'can not load data'); // here iam catching the error if there is one
}
}
}
here is the json i did with the constructor and named constructor
// this is the custom Response object
class Post {
late int userId;
late int id;
late String title;
late String body;
// normal Constructor
Post({
required this.userId,
required this.id,
required this.title,
required this.body,
});
//named Constructor
//factory is meaning that i do not want to create a new object every time but to use escesting one
factory Post.fromjson(Map<String, dynamic> jason) {
return Post(
id: jason['id'],
userId: jason['UserId'],
title: jason['title'],
body: jason['body'],
);
}
}
I like how you call json as jason makes it more personal, moreover the issue is most probably due to a typo here userId: jason['UserId'] because your screenshot shows it as userId: jason['userId'].
I have a problem with parsed JSON in Flutter-Dart. Actually, there is no problem, but there is a method which I don't know. I'm getting data from a PHP server and I'm writing to listview from parsed JSON. However, I don't want to write to a listview, I just want to get data from the parsed JSON because I will set an another textview from parsed json data which I get.
This is my code.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response =
await client.get(Uri.parse('https://meshcurrent.online/get_20word.php'));
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final String questId;
final String quest;
final String ans1;
final String ans2;
final String ans3;
final String ans4;
final String correctAns;
final String category;
const Photo({
required this.questId,
required this.quest,
required this.ans1,
required this.ans2,
required this.ans3,
required this.ans4,
required this.correctAns,
required this.category,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
questId: json['questId'] as String,
quest: json['quest'] as String,
ans1: json['ans1'] as String,
ans2: json['ans2'] as String,
ans3: json['ans3'] as String,
ans4: json['ans4'] as String,
correctAns: json['correctAns'] as String,
category: json['category'] as String,
);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const appTitle = 'Isolate Demo';
return const MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(children: [
Text("ronalddo"),
Text("mecsfsi"),
FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
return PhotosList(photos: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
]));
}
}
class PhotosList extends StatelessWidget {
const PhotosList({Key? key, required this.photos}) : super(key: key);
final List<Photo> photos;
#override
Widget build(BuildContext context) {
return ListView.builder(
padding: EdgeInsets.all(20),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: photos.length,
itemBuilder: (context, index) {
return ListTile(
title: Row(
children: [
Text(photos[index].quest),
Text(photos[index].ans1),
],
),
);
});
}
}
I want to do get only first column and get only questId from this parsed JSON. And I will set a textview using Getx library. I know set a data with Getx library but I can't do it because I don't know get the data.
If you just want to get the first questId of the List, do
photos[0].questId;
I want to use the SWAPI API in my flutter project but the snapshot has no data (it's null). No key is needed and when I click on the website I get the json data.
I did everything like in this documentation https://docs.flutter.dev/cookbook/networking/background-parsing.
persons_page.dart:
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:star_wars/models/persons.dart';
class PersonsPage extends StatefulWidget {
const PersonsPage({ Key? key }) : super(key: key);
#override
State<PersonsPage> createState() => _PersonsPageState();
}
class _PersonsPageState extends State<PersonsPage> {
Future<List<Persons>> fetchPersons(http.Client client) async {
final response = await client.get(Uri.parse('https://swapi.dev/api/people/1'));
return compute(parsePersons, response.body);
}
List<Persons> parsePersons(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String,dynamic>>();
return parsed.map<Persons>((json) => Persons.fromJson(json)).toList();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<List<Persons>>(
future: fetchPersons(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
print(snapshot.data);
return const Center(
child: Text("An error has occurred!"),
);
} else if (snapshot.hasData) {
return PersonsList(persons: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
class PersonsList extends StatelessWidget {
const PersonsList({ Key? key, required this.persons }) : super(key: key);
final List<Persons> persons;
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: persons.length,
itemBuilder: (context, index) {
return Text("Got it");
},
);
}
}
persons.dart:
class Persons {
final String name;
final String height;
final String mass;
final String hairColor;
final String skinColor;
final String eyeColor;
final String birthYear;
final String gender;
final List films;
final List vehicles;
final List starships;
const Persons({
required this.name,
required this.height,
required this.mass,
required this.hairColor,
required this.skinColor,
required this.eyeColor,
required this.birthYear,
required this.gender,
required this.films,
required this.vehicles,
required this.starships,
});
factory Persons.fromJson(Map<String, dynamic> json) {
return Persons(
name: json['name'] as String,
height: json['height'] as String,
mass: json['mass'] as String,
hairColor: json['hair_color'] as String,
skinColor: json['skin_color'] as String,
eyeColor: json['eye_color'] as String,
birthYear: json['birth_year'] as String,
gender: json['gender'] as String,
films: json['films'] as List,
vehicles: json['vehicles'] as List,
starships: json['starships'] as List,
);
}
}
the console output is:
Reloaded 1 of 632 libraries in 474ms.
I/flutter ( 6328): null
i don't know what is wrong with the code :/
I am trying o get data from coinmarketcap api but i seem to be getting the error above. included is my main.dart code, this is my first time using flutter/dart, so i don't quite understand everything, i followed this guide from flutter docs https://docs.flutter.dev/cookbook/networking/background-parsing , but i still got some errors that i then tried to solve by following this NoSuchMethodError: Class'_InternalLinkedHashMap<String, dynamic>'has no instance method 'cast' with matching arguments
Can anyone help me?
The error i get is this on line 22:
NoSuchMethodError (NoSuchMethodError: Class 'CastMap<String, dynamic, String, dynamic>' has no instance method 'call'.
Receiver: Instance of 'CastMap<String, dynamic, String, dynamic>'
My dart file:
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Criptos>> fetchcriptos(http.Client client) async {
final response = await client.get(
Uri.parse(
'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?id=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15'),
headers: {
"X-CMC_PRO_API_KEY": "ecfc8e0a-fd11-422d-8a7b-114e8b31e62c",
"Accept": "application/json"
});
return compute(parsecriptos, response.body);
}
List<Criptos> parsecriptos(String responseBody) {
final parsed = jsonDecode(responseBody).cast<String, dynamic>();
return parsed<Criptos>((json) => Criptos.fromJson(json)).toList();
}
class Criptos {
final int id;
final String name;
final String symbol;
final int max_supply;
final int cmc_rank;
final int preco;
const Criptos({
required this.id,
required this.name,
required this.symbol,
required this.max_supply,
required this.cmc_rank,
required this.preco,
});
factory Criptos.fromJson(Map<String, dynamic> json) {
return Criptos(
id: json['data']['id'] as int,
name: json['data']['name'] as String,
symbol: json['data']['symbol'] as String,
max_supply: json['data']['max_supply'] as int,
cmc_rank: json['data']['cmc_rank'] as int,
preco: json['data']['quote']['USD']['price'] as int);
}
}
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
const appTitle = 'Isolate Demo';
return const MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Criptos>>(
future: fetchcriptos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
return ListaCriptos(cripto: snapshot.data!);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
}
class ListaCriptos extends StatelessWidget {
const ListaCriptos({Key? key, required this.cripto}) : super(key: key);
final List<Criptos> cripto;
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: cripto.length,
itemBuilder: (context, index) {
return Text(cripto[index].id);
},
);
}
}
Github of the project
So I'm trying to build a list (of any kind) from a Json list object in flutter, I'm getting it using REST api the response is a Json list with the fields: type, contact {first_name, last_name}, created_at, uuid.
using this code I'm fetching the data and parsing it to custom data type
import 'dart:convert';
import 'package:connectix/models/calls.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
class ContactService {
static String _url = "https://api.voipappz.io/tasks//connectix_conversations_recent";
static Future browse() async{
var channel = IOWebSocketChannel.connect(Uri.parse(_url));
}
}
List<CallType> parseCalls(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<CallType>((json) => CallType.fromJson(json)).toList();
}
Future<List<CallType>> fetchCalls(http.Client client) async {
final response = await client
.get(Uri.parse("https://api.voipappz.io/tasks//connectix_conversations_recent"));
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parseCalls, response.body);
}
That's the data type model
class CallType {
final String type;
final String first_name, last_name;
final String created;
CallType({
required this.type,
required this.first_name,
required this.last_name,
required this.created,
});
factory CallType.fromJson(Map<String, dynamic> json){
return CallType(
type: json['type'],
first_name: json['contact.first_name'],
last_name: json['contact.last_name'],
created: json['created_at'],
);
}
}
and this is the code for the widget I'm trying to display and returns me the error in the question title
class CallsList extends StatelessWidget {
final List<CallType> calls;
CallsList({Key? key, required this.calls}) : super(key: key);
#override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: calls.length,
itemBuilder: (context, index) {
return Text(calls[index].type);
},
);
}
}
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.orange,
appBar: AppBar(
title: Text('hello'),
),
body: FutureBuilder<List<CallType>>(
future: fetchCalls(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? CallsList(calls: snapshot.data!)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
You marked all attributes of CallType as required. It looks like the API is not returning data for one (or more) of these attributes.
You need either to mark them as optional (remove required and make them of type String?), or take care that your API is always responding with data.