While developing apps with Flutter, you most likely will have to use JSON in one form or another. In this article I will explain how to parse JSON, serialize data into JSON, and some advanced concepts.
What is Flutter?

Flutter is a mobile development framework that allows you to create a cross-platform app, meaning the same code will run on both Android and IOS. This article assumes some knowledge of Flutter. If you have never worked with it, I suggest reading this article first and completing these tutorials before continuing.
How to serialize JSON
Firstly, let’s talk about how to serialize data into JSON in Flutter. To do this, you need to import dart:convert
. It is a built-in library that provides a simple API for JSON creation. To import it, add this line at the top of your file:
import 'dart:convert';
Now, you can encode any number
, boolean
, string
, null
, list
, or map
. This is done using the jsonEncode
function:
Map<String, String> user = {"name": "John Doe", "email": "[email protected]"};
String userJson = jsonEncode(user);
Now you have a JSON-encoded string you can send to your API. Beware that this will work only with the types I listed earlier. For more types and/or classes, you will need to implement a custom serializer, keep reading to check it out.
How to parse JSON
We will need the same library, dart:convert
to parse JSON in Flutter. To do this, you can use the jsonDecode
function:
String userJson = '{"name": "John Doe", "email": "[email protected]"}';
Map<String, dynamic> user = jsonDecode(userJson);
print(`Hi, ${user['name']}`); // Hi John Doe
This will work with any valid JSON string. There is a downside, however: the return type is a generic Map
, with dynamic
values. This means the types are not strong, will be validated during run time and we lose a lot of type safety perks. To parse JSON into your models safely, you will need a custom parser.
Writing custom serializer and parser
As your app grows, you will create more and more models (classes). To adapt these classes for JSON, you need to do 2 things: create a new constructor function fromJson
and a method toJson
. Check out this simple example:
On line 8 you can see the fromJson
constructor. It behaves almost the same as the regular constructor, just takes values of different type. On line 12 there is the toJson
method. It returns a Map
that can be used by jsonEncode
to produce a JSON string. Here it is in action:
Using this simple API, you can easily turn any class into a JSON-compatible class.
Using code generation tools
What if I said that you can further simplify this process? The thing is, you can. There are special libraries that would write the conversion code for you (a concept called code generation). To do this, first, add some dependencies to your pubspec.yaml
:
dependencies:
# your other deps
json_annotation: ^3.0.0
dev_dependencies:
# your other deps
build_runner: ^1.0.0
json_serializable: ^3.2.0
Now, let’s slightly modify the Car
class from earlier:
Note the import on line 1. It includes the annotations (decorators) for classes. Now, line 3 is confusing. The code generation tool will produce a car.g.dart
. This line allows us to access its private properties.
On line 5 we wrap the class with JsonSerializable
. It will scan our classes’ fields and produce both a serializer and a parser. Note that our class properties are no longer final, since these methods will be changing them.
On line 13, examine the changes to the custom constructor. It now has the factory
modifier. It indicates that it may not return a new instance of the Car
class and is necessary for JsonSerializable
. Also, instead of parsing json ourselves, we call _$CarFromJson
. This method is generated and imported on line 3. The same with toJson
: we are using _$CarToJson
.
The exiting thing is, there are no changes whatsoever to the actual usage code after refactoring a class like this. It means you can mix and match approaches based on the needs of your app, and leave the interface intact.
Closing notes
Thank you for reading, I hope I helped you work with JSON in Flutter. This article is of course not exhaustive, there is much more to learn about, like per-property serializers, default values, and error handling. Stay tuned for future posts!
I was using nested model class and got the output on UI as ‘instance of address ‘
how to get the nested address printed on the app?