JSON Encode / Decode with Flutter
JSON Encode / Decode with Flutter
Summary
Are you working on a Flutter app and need to interact with structured data that is not local to your project? Most likely, you will be fetching it from a web service API or a file. For the purpose of this guide, we’ll assume that the data you have is in JavaScript Object Notation or JSON format.
- How do you decode the JSON data into an Object that is usable in your app?
- How do you encode an Object into JSON so that you can store it?
- Are there any restrictions on how to encode or decode JSON in Flutter?
We’ll cover all of these questions and more in the following discussion.
For brevity, we will not go into the details of how to set up a web service in this guide. For a crash course on web services with Spring Boot take a look at our Spring Boot REST API crash course.
What’s the scoop?
Believe it or not, Flutter does not actually support JSON directly. Instead, JSON is interpreted as a map; Map<string, dynamic>
to be exact. The reason for this is that a regular library for encoding and decoding JSON data would require the use of reflection. Currently, reflection causes a conflict with tree shaking, therefore, Dart libraries like dartson will not work with Flutter even though it is valid for other Dart projects.
For a small number of objects, Flutter recommends to manually encode/decode your class (Models). There are a few libraries that can help with serialization and deserialization but they require some setup and rely on code generation NOT runtime translation.
We are going to use the dart:convert library to manually translate values to/from our User
Model.
Encoding User to JSON
The jsonEncode
method from dart:convert
will automatically call the toJson
method of a class that has it defined. So, let’s open up our User class and add toJson
.
class User {
final String firstName, lastName, website;
const User(this.firstName, this.lastName, this.website);
}
Our new toJson
has a return type of Map<String, dynamic>
where the String is the “key” and dynamic is the “value” of the class field we want to convert. To demonstrate this, let’s use a “key” that is not the same as the field definition. Instead of the “firstName” (field) we will use “first_name” as the JSON key.
class User {
final String firstName, lastName, website;
const User(this.firstName, this.lastName, this.website);
Map<String, dynamic> toJson() => {
"first_name": this.firstName,
"last_name": this.lastName,
"website": this.website
};
}
Decoding User from JSON
The jsonDecode
method from dart:covert
will automatically call the fromJson
method of the contained object class. In this example, we will add our own fromJson method to our User
class; in this method, we will extract the data from a Map of type Map<String, dynamic>
and create a new instance of User
.
Notice that fromJson
is defined as a named constructor and returns a new instance of our User.
class User {
final String firstName, lastName, website;
const User(this.firstName, this.lastName, this.website);
User.fromJson(Map<String, dynamic> json):
this.firstName = json['first_name'],
this.lastName = json['last_name'],
this.website = json['website'];
Map<String, dynamic> toJson() => {
"first_name": this.firstName,
"last_name": this.lastName,
"website": this.website
};
}
What if I have a collection of User objects?
Unfortunately, we want to have a collection of objects we cannot simply use the same dart:convert
methods, at least not directly. Instead, we need to create another class UserList
; in this class, we will have our collection of User
objects and then call the respective toJson
and fromJson
methods.
class UserList {
final List<User> users;
UserList(this.users);
UserList.fromJson(List<dynamic> usersJson) :
users = usersJson.map((user) => User.fromJson(user)).toList();
}
Simply iterate over the List of Users, List<User>
, creating an instance of User
for each JSON data and then finally set our collection as a List.
Now, let’s put this into action. As an example, we are reading a JSON array of User from a file and then decoding the JSON into a List<User> which will be rendered in a scrolling view.
class UserProvider {
final String _dataPath = "assets/data/users.json";
List<User> users;
Future<List<User>> loadUserData( ) async {
var dataString = await loadAsset();
Map<String, dynamic> jsonUserData = jsonDecode(dataString);
users = UserList.fromJson(jsonUserData['users']).users;
return users;
}
Future<String> loadAsset() async {
return await rootBundle.loadString(_dataPath);
}
}
Manually creating the methods for (de)serialization gets old very fast; especially when the Models become complex. I’m personally excited to see the JSON tooling for Flutter become more mature.
In this guide, we did not cover the term “Provider” to learn how/why you might use Provider to manage state in your Flutter app, see our Using Provider in Flutter guide.