Tomáš Repčík - 1. 7. 2025

Flutter at Scale: The Monorepo Playbook

Monorepo made my life easier

Flutter at Scale: The Monorepo Playbook

Most likely, you have a lot of repositories stored on GitHub or other services.

At your company, you have a lot of repositories, which are used by different teams.

However, some tools and packages are used by multiple people, projects, and it is hard to keep them in sync.

Another issue is that, when you want to develop an app and package at the same time, you need to have two separate repositories and going from project to project is just tedious.

Most of the arguments for such a split are:

However, if you work at the same company, developing the same business logic and using the same packages, it can feel like doing a lot of unnecessary work.

If Meta can do it for their whole codebase, why not you?

Monorepo

With a single monorepo, you can have all the projects in one place. You can scope the IDE to the specific project you are working on, but you can still see all the other projects in the same repository.

If you need to change something in the package, you can do it right away and test it in the app you are working on.

Mainly, if you develop a lot of projects, you can fix and update the packages in one place. There is less friction in the development process because you do not have this overhead of creating branches, pull requests, and merging them into the main branch.

Someone might argue that there is a lot of maintenance work to do, but if you want to have high-quality code, you need to do it anyway.

The same applies to the dependencies - you need to keep them up to date, and it is easier to do it in one place than in multiple repositories.

Monorepo and AI

Since we are moving towards the age of AI, if you have all the code in one place, you can use AI tools to help you with the development process.

Agents can be used to analyze the codebase, find issues, and suggest improvements. They can also help you with the code generation, which can save you a lot of time.

The best part of it is that you do not need to add any external context via some API or other means, because the AI agent has access to the whole codebase in one place.

If you think that AI is not able to work with large codebases, you are wrong. There are already AI agents that can work with large codebases and help you with the development process.

Monorepo in Flutter

Flutter can work as a monorepo just out of the box.

You can create a single repository, split it into multiple folders and use the pubspec.yaml file to manage the dependencies in one of the apps.

name: my_app
description: A Flutter application that uses multiple packages from the same monorepo.
version: 1.0.0+1
dependencies:
  flutter:
    sdk: flutter
  my_package:
    path: ../../packages/my_package
  another_package:
    path: ../../packages/another_package

I usually divide the repository into:

But feel free to divide them as you want.

A couple of ideas for the packages:

Melos and Flutter

Melos is a tool that helps you manage monorepos in Flutter. It provides a set of commands to help you with the development process.

For the setup, visit their documentation.

Instead of repeating steps from the documentation, which can get outdated, I will just show you how to use it in your monorepo.

In the root of your monorepo, you need to create a melos.yaml file. This file contains the configuration for Melos.

Here you can write your scripts, which can automate your development process at scale through multiple apps and packages.

Simple Flutter Commands

During the development, you use commands like flutter get, flutter analyze, flutter format and others, you can add them to the melos.yaml file to run them in all the apps and packages in the monorepo.

scripts:
  analyze: melos exec -- dart analyze . --fatal-infos
  format: melos exec -- dart format . # this can be done by melos format as well
  get: melos exec -- flutter pub get

You can also run a lot of already built-in commands in Melos like:

melos format
melos clean

Code generation

In my projects, I use a lot of code generation like Freezed, JsonSerializable, Mockito and many others. If you move on to a new version of the package, you need to regenerate the code in all the apps and packages that use it.

Melos provides a command to run the code generation in all the packages and apps in the monorepo.

scripts:
  generate: >
    melos exec --order-dependents --fail-fast --depends-on="build_runner" -- \
      dart run build_runner build -d

A couple of notes:

Linting and Formatting All the Code

We can format the code via Melos, but what if you want to add a new lint rule or update built.yaml file?

You can use the melos exec command to run the linting and formatting in all the packages and apps in the monorepo.

  apply_analysis_options: >
    melos exec -- \
      cp ../../analysis_options.yaml ./

  apply_build_yaml: >
    melos exec -- \
      cp ../../build.yaml ./

Place the analysis_options.yaml and build.yaml files in the root of your monorepo, and then run the commands to apply them to all the packages and apps in the monorepo.

You can chain it with the format command to format the code after applying the new rules.

Testing

You can also run the tests and split them into unit and widget tests.

scripts:
  unit_test:
    run: |
      melos exec --fail-fast-- \
        "flutter test"
    packageFilters:
      dirExists:
        - test
      ignore:
        - '*example*'
  widget_test:
    run: |
      melos exec --fail-fast -- \
        "flutter test integration_test"
    packageFilters:
      dirExists:
        - integration_test

Backend dependencies

If you are using a backend of some sort and by some chance it is in a monorepo, you can use openapi generator to generate the code for the backend in all the apps and packages in the monorepo.

scripts:
  generate_openapi:
    run: |
      melos exec -- \
        <your set of commands to generate the code for Flutter>

Licensing

If you want to distribute licenses to the website or for some bureaucratic reason, you can use the flutter_oss_license package to generate the licenses into a file.

scripts:
  generate_oss_license:
    run: |
      melos exec -- \
        flutter pub run flutter_oss_license:generate

When not to use a monorepo

Monorepo is an option, not a silver bullet, and it is not for everyone.

If you have domain-specific projects, it is better to keep them in separate repositories.

If you do not plan on long-term maintenance of the codebase, it is better to keep it in separate repositories.

For one-off projects, it is better to keep them in separate repositories, too.

Conclusion

Here are some of the benefits of the monorepo approach. Suddenly, a lot of the operations on the codebase can be automated, and there is less hassle in the development process.

Yes, it requires more setup and from time to time more maintenance, but it pays off in the long run in saved time and effort. If you maintain a lot of code, it is worth it.

Personally, I use my packages to build and update multiple apps at the same time. It saves me time in fixing issues and keeping everything updated, and not forgetting about something.

You can look at Flutter alone - it is a monorepo, which contains all the packages and apps in one place.

With progress in AI, it is easier to use AI tools to help you with the development process, because you have all the code in one place, in one context.

If you liked the article, do not forget to follow me for more.

Subscribe for more
LinkedIn GitHub Medium X