Flutter: How Code Generators Work

Learn how packages like freezed, json_serializable, and riverpod_generator work under the hood

Tomic Riedel
4 min readMay 20, 2024
Icon by freepik

Being a Flutter developer, I’d bet you have used at least once a code generation tool. Have you ever used freezed? json_serializable? riverpod_generator? mobx_codegen? Or have you ever run dart run build_runner build? If your answer is yes, you have already used a code generation tool in Flutter.

But… what exactly are code generators? And most importantly, how do they work?

All of that, we are going to cover in today’s article.

· What Are Code Generators?
· The Role of Code Generators in Flutter
· How Code Generators Work

What Are Code Generators?

Code generation in software development refers to the process of automatically generating code that is typically written by developers.

This is achieved by using tools and scripts that convert specific inputs or models into source code that can be compiled and executed. These inputs might include domain-specific languages, annotations in code, or model descriptions. The primary aim of code generation is to automate repetitive coding tasks, reduce human errors, and streamline the development process.

Now that we know what code generators are, why do we even need them in Flutter?

The Role of Code Generators in Flutter

Believe it or not, code generators are one of the most essential tools you have in Flutter development.

They help you with a lot of things, including

  1. Serialization/Deserialization: Automatically generating code to handle JSON or other data formats. This is useful in Flutter applications that consume web services where data interchange formats like JSON are commonly used.
  2. Dependency Injection: Flutter uses code generation to facilitate dependency injection, which helps in managing the dependencies between objects in a more scalable way.
  3. Immutable State Management: Tools like built_value or freezed help in generating immutable state objects, which can be useful for state management in Flutter apps, ensuring that the state is predictable and easy to debug.
  4. Routing: Code generation can be used to handle navigation and routing in a Flutter application, making it easier to manage complex navigation flows.
  5. Local Database Handling: For local storage, code generation can automate the creation of boilerplate code for database operations, interfacing with SQLite databases, for example.

And a lot more…

And the advantages of using them are pretty clear. You reduce the boilerplate code you have to write, and your accuracy is improving because an algorithm writes the code, it can speed up your development process and make it easier to maintain & scale the code.

How Code Generators Work

Let’s finally take a look at how code generators work.

1. Annotations

Every code-gen library uses annotations to mark the classes or fields that need code generation. Annotations are metadata added to the code, which doesn’t affect the runtime behavior directly but provides information about how code should be generated.

2. Source Code Analysis

Code generators analyze your Dart code to understand which parts are marked for generation. This involves parsing the source files to create an abstract syntax tree (AST), which represents the structure of the code in a way that the generator can work with. Tools in Dart, like the analyzer package, are used to parse and traverse the AST.

Luckily, if you would have to write your own code generator package, you don’t have to worry too much about parsing the actual code.

3. Code Generation Logic

Based on the AST and the specific annotations found, the code generator applies its logic to produce new Dart code. This logic is essentially a series of rules defined by the developers of the library, which determine how to translate annotations and associated code into new Dart files. This may involve:

  • Generating serialization/deserialization logic for data classes (json_serializable).
  • Creating immutable classes with copyWith methods (freezed).
  • Building boilerplate for state management systems (like mobx for MobX stores).

It all comes down to the use case.

4. Template Engine

Most code generators use some form of templating engine to produce the final code. These templates define the structure of the output code, with placeholders that get filled in based on the specifics of the annotated code. This step ensures that the generated code is syntactically correct and follows the conventions of Dart programming.

5. Build System Integration

Flutter/Dart code generation often integrates with the build system through tools like build_runner. This tool watches for changes in the source files and triggers the code generation process automatically during development. It uses:

  • Builders: These are scripts that define how to generate code from input files and where to output the result.
  • Build Actions: These configure which builders to run and which files they should apply to.

6. Output

The output of this process is typically Dart source files that are automatically generated and included in your project. These files are typically marked as part of the .dart_tool/ directory or directly alongside the user's source files with a distinguishable naming pattern (like filename.g.dart).

Conclusion

That’s it for today. As already said, this article was about how code generators work. Not how you can create your own. If you want me to create an article about that, just leave a comment and I’ll try my best!

Btw, since you have read this article, these articles might interest you too:

Bye 👋

--

--

Tomic Riedel
Tomic Riedel

Written by Tomic Riedel

Sharing the process of building a portfolio of apps to make people more productive.

No responses yet