I am very new to Flutter and have been trying for a few days now to get this right. Either I get stuck with the set state that does not reload(With a spinner) or the JSON cant is decoded as my example.
I want to get the JSON into a listview and then when the floatingActionButton button is pushed refresh the widget with the CircularProgressIndicator. This is what I have so far. All examples that I find seem not to be Null safe and again I am lost.
My example says "List' is not a subtype of type 'Map<String, dynamic>" and I can see this is because my JSON is a list List ??.
A few pointers would be greatly appreciated.
This is the JSON its pulling:
[
{
"userId": 1,
"id": 1,
"title": "How to make friends"
},
{
"userId": 2,
"id": 1,
"title": "Gone with the wind"
}
]
```
Future<Album> fetchAlbum() async {
print("Fetching json....");
final response = await http.get(
Uri.parse(myLocksEp),
);
if (response.statusCode == 200) {
print(response.body);
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
class Album {
final int id;
final String title;
Album({required this.id, required this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
id: json['id'],
title: json['name'],
);
}
}
void main() {
runApp(mylocks());
}
class mylocks extends StatefulWidget {
@override
_MyAppState createState() {
return _MyAppState();
}
}
class _MyAppState extends State<mylocks> {
late Future<Album> _futureAlbum;
@override
void initState() {
super.initState();
_futureAlbum = fetchAlbum();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(8.0),
child: FutureBuilder<Album>(
future: _futureAlbum,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[],
);
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
//Error message goers here
}
}
return const CircularProgressIndicator();
},
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh_outlined, color: Colors.black),
backgroundColor: Colors.grey[200],
onPressed: () {
setState(() {
_futureAlbum = fetchAlbum();
});
},
),
),
);
}
}
Sorry for the long post, I hope I make sense
Thank you in advance Robby
CodePudding user response:
If I'm not wrong, here you are providing a List<Map<String, dynamic>>
return Album.fromJson(jsonDecode(response.body));
while the Album.fromJson is expecting to receive a Map<String, dynamic> So, thats why you are getting the error "List' is not a subtype of type 'Map<String, dynamic>"
Apart from that, take into account that every time you build the widget, FutureBuilder will always make a call to the future that you pass to it, so in this case, when you press the FloatingActionButton, you will make a call, and then, when the widget start to rebuild, you will make another one.
You should make some adjustments to avoid this. You could change the FloatingActionButton onPressed callback to be empty:
setState(() {});
Again, if I'm not wrong this will make the widget rebuild itself since you are saying that the state has changed, and when rebuilding, the FutureBuilder will make the request, and therefore, update the list.
EDIT BASED ON THE COMMENTS
class _MyAppState extends State<mylocks> {
late Future<Album> _futureAlbum;
//ADD ALBUMS VAR
List<Album> _albums = [];
@override
void initState() {
super.initState();
//INVOKE METHOD AND SET THE STATE OF _albums WHEN IT FINISHES
fetchAlbum().then((List<Album> albums){
setState((){_albums = albums);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(8.0),
child: Column(
//REPLACE FUTURE BUILDER WITH DESIRED WIDGET, WHILE _albums IS EMPTY IT WILL RENDER A CircularProgressIndicator
children: [
_albums.isEmpty ? Center(
child: CircularProgressIndicator(),
) : ..._albums,
]
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.refresh_outlined, color: Colors.black),
backgroundColor: Colors.grey[200],
onPressed: () {
//AGAIN, WHEN FINISHING IT SHOULD REBUILD THE WIDGET AND THE DATA
fetchAlbum().then((List<Album> albums){
setState((){_albums = albums);
});
},
),
),
);
}
}
CodePudding user response:
Don't worry about creating models for your Json just use this link to autogenerate a model.