Reputation: 81
I'm building an events app using Flutter that fetch data from an Rails api and show its details. I got the same error as this question, and already tried the answer from "kapsid", but can't solve yet.
My model class (evento.dart). Basically it represents a Larger event with multiple small events in it.
import 'package:flutter/material.dart';
class Evento {
int id;
String titulo, descricao, inicio, fim, dataFimInscricao;
List<Evento> children;
Evento(
{this.id,
this.titulo,
this.descricao,
this.inicio,
this.fim,
this.dataFimInscricao,
this.children});
factory Evento.fromJson(Map<String, dynamic> json) {
var list = json['children'] as List;
List<Evento> childrenList = list.map((c) => Evento.fromJson(c)).toList();
return new Evento(
id: json['id'],
titulo: json['titulo'],
descricao: json['descricao'],
inicio: json['inicio'],
fim: json['fim'],
dataFimInscricao: json['data_fim_inscricao'],
children: childrenList
);
}
The main.dart to list various similar large events
import 'dart:convert';
import 'package:eventos_app/detalhesEvento.dart';
import 'package:eventos_app/model/evento.dart';
import 'package:eventos_app/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<Evento> futureEvento;
@override
void initState() {
super.initState();
futureEvento = fetchEvento();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: FutureBuilder(
future: futureEvento,
builder: (context, snapshot) {
if (!snapshot.hasData) {
print(snapshot.error);
return Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Text(snapshot.error.toString()),
),
);
} else {
var eventos = json.decode(snapshot.data.toString());
return ListView.builder(
itemCount: eventos.length,
itemBuilder: (BuildContext context, int index) {
return Card(
margin: EdgeInsets.all(8),
elevation: 5,
child: ListTile(
contentPadding: EdgeInsets.all(8),
leading: Icon(
Icons.local_florist,
size: 35,
),
title: Text(eventos[index]['titulo']),
subtitle: Text(
'Data: ${parseDate(eventos[index]['inicio'])}\n${eventos[index]['endereco']}'),
onTap: () {
var evento = Evento.fromJson(eventos[index]);
print(evento.titulo);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
DetalhesEvento(evento: evento)));
// Scaffold.of(context).showSnackBar(SnackBar(
// content: Text('${event.title} tapped!'),
// duration: new Duration(seconds: 1)
// ));
},
),
);
},
);
} // else
},
),
),
);
}
}
Future<Evento> fetchEvento() async {
final response =
await http.get('https://api.myjson.com/bins/1d8142');
print('----------------------------------------\n');
print(response.body);
print('\n----------------------------------------');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
// return Evento.fromJson2(json.decode(response.body));
return Evento.fromJson(json.decode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load events');
}
}
And this is the sample JSON
[
{
"id": 1,
"titulo": "MAIN EVENT",
"descricao": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In elementum metus erat. Morbi sit amet pellentesque elit. Sed at gravida nisi. In in diam in mi gravida iaculis. Phasellus ac leo vel arcu faucibus porta at nec elit. Proin imperdiet leo congue, mattis nunc non, laoreet turpis. Morbi sed faucibus lorem.",
"inicio": "2020-03-13T10:00:00.000-03:00",
"fim": "2020-03-31T23:59:00.000-03:00",
"data_fim_inscricao": "2020-03-31T23:59:00.000-03:00",
"children": [
{
"id": 4,
"titulo": "SECONDARY EVENT AAA",
"descricao": "AAAAA",
"inicio": "2020-03-24T08:00:00.000-03:00",
"fim": "2020-03-25T18:00:00.000-03:00",
"data_fim_inscricao": "2020-03-23T08:00:00.000-03:00",
"children": [
]
},
{
"id": 3,
"titulo": "SECONDARY EVENT BBB",
"descricao": "BBBBB",
"inicio": "2020-03-13T23:59:00.000-03:00",
"fim": "2020-03-25T23:59:00.000-03:00",
"data_fim_inscricao": "2020-03-13T23:59:00.000-03:00",
"children": [
]
},
{
"id": 2,
"titulo": "SECONDARY EVENT CCC",
"descricao": "CCCCC",
"inicio": "2020-03-14T10:00:00.000-03:00",
"fim": "2020-03-16T23:59:00.000-03:00",
"data_fim_inscricao": "2020-03-16T23:59:00.000-03:00",
"children": [
]
}
]
}
]
What I'm doing wrong??
Upvotes: 1
Views: 390
Reputation: 3744
The problem is a mismatch between the data that you've received from how the data is expected to be received. Specifically, you're trying to pass response.body with is of type List<dynamic>
to a function that is looking for Map<String, dynamic>
I can't run your code, but it looks like the problem is happening on this line in FetchEvento:
return Evento.fromJson(json.decode(response.body)); // <-- you're passing List<dynamic>
because you are passing response.body with type List<dynamic>
to Evento.fromJson()
which is expecting a variable of type Map<String,dynamic>
factory Evento.fromJson(Map<String, dynamic> json) { //<-- you defined json as Map<String,dynamic>
Because dart is a strongly typed language, they have to be defined as the same type.
The easiest solution would either be one of three things: 1. The easiest would be to change the type of variable received to:
factory Evento.fromJson(List<dynamic> json) {
2. However, that might break something else. (why else would it be defined as Map<String, dynamic>
?) Therefore, the second thing to do would be to manipulate response.body from a List<dynamic>
to Map<String,dynamic>
inside fetchEvento()
prior to sending it to Evento.fromJson()
by doing something similar to:
var map1 = Map.fromIterable(response.body, key: (e) => e.name, value: (e) => e.value);
(you can read more about that here: https://bezkoder.com/dart-convert-list-map/)
I hope that it's option 1!
Upvotes: 1