Working with JSON in Flutter While Using json_annotations and json_serializable Modules in 2022

How to use Flutter/Dart’s magical ways to convert JSON data into usable structures so that you can get unstuck — and get going.

Why are you here?

You have read tons of Medium posts and watched plenty of YouTube videos and still feel a little confused. Along the way, you found various ways to convert JSON into Dart code magically. But … it doesn’t work quite right.

This is the best converter out there, but it’s not perfect. But it does a great job of locating the general ENUMs you might want to use … and also the rough data structure you’ll want to have going forward.

Let’s do this

Follow along in 5-ish steps:

1. Add three modules to your project from the command-line:

unix% flutter pub add json_serializable
unix% flutter pub add json_annotation
unix% flutter pub add build_runner

and then adjust your pubspec.yaml file by moving two of the entries underneath dev_dependencies to look like so:

From the main github repo for json_serializable

2. Any place you intend to use this module you’ll need this header for your file (let’s say the file’s named special.dart):

import 'package:json_annotation/json_annotation.dart';

3. Your special.dart file needs a line of code that reads:

part 'special.g.dart'

Note that weird “.g” you need to add — be sure to match this statement with your actual file name. You’ll thank me later.

4. Each time you define a class with data inside it, it will have the format:

@JsonSerializable()

class Person {

// required vs optional distinction gets made by whether
// you say it's nullable? or not / you'll need to match it
// up with what you stick in your constructor / required
// means it's necessary; and if it's optional, you need
// to nullable? the instance variable explicitly

  final String firstName, lastName;
  final DateTime? dateOfBirth;

  Person({
    required this.firstName, 
    required this.lastName, 
    this.dateOfBirth}); 

// replace '<your class>' where it says Person / these
// two lines of code are boilerplate that you can either
// automate with a VS Code or Android Studio plugin or
// resort to doing it manually ...

  factory Person.fromJson(Map<String, dynamic> json) => 
     _$PersonFromJson(json);

  Map<String, dynamic> toJson() => 
     _$PersonToJson(this);
}

Note that step 4’s aspect of: 1/ adding the factory, and 2/ Map calls towards the end require leap-of-faithing and adherence to changing those three (3) Person spots with diligence. Don’t think about it too much.

5. Run the code generator with:

flutter pub run build_runner build

After you run this, all the error messages should have gone away.

Using the cool enum capabilities

  1. Layout the enum explicitly like this:
@JsonEnum()
enum Noun {
  @JsonValue('image') image,
  @JsonValue('text') text,
  @JsonValue('desktop') desktop,
}
  1. Subsequently you can just refer to Noun and any text that reads image or text or desktop gets converted into Noun.image or Noun.text or Noun.desktop internally.

Debugging when things go bad

Glad you asked! I have no idea how to debug when things go wrong with this process. It can get quite contorted. In particular when you:

  1. Basic problem: Have any JSON keys that don’t match the required list
  2. Deluxe problem: Have enums out of scope in your fancy @JsonEnum step

I scratch my head a little. If anyone knows out there how to do this best, do LMK. —JM

Need a JSON viewer on OS X?

This one is quite nice: https://jsonviewer.app/