Flutter: guide for using FVM with Melos

Tamás Menyhárt
6 min readMay 28, 2024

--

Introduction

Before introducing a new technology stack to a team, it is advisable to assess the availability of third-party developer tools. In the long term, selecting appropriate tools can ensure that the time and resources invested are worthwhile.

Regarding Flutter/Dart, our team has identified two developer tools that have significantly enhanced our developer workflow and project scalability. While each tool is relatively easy to use independently, integrating them requires some additional configuration.

FVM

One of the tools is Flutter Version Manager, which simplifies the process for developers managing multiple projects with different Flutter versions. This tool allows developers to switch SDK versions on demand, enhancing their workflow efficiency.

Melos

The other tool is Melos, which facilitates the management of code repositories containing multiple packages or applications, known as monorepos. Melos assists developers in handling local package dependencies and provides mechanisms to execute various scripts within the context of selected packages or applications.

Installation

Both tools are Dart packages, requiring a system-wide Dart installation. On macOS, Dart can be easily installed using the Homebrew package manager:

brew install dart

You can verify the successful installation of Dart by running the command dart --version which should output the installed Dart version.

The tools can be installed using Dart and Pub, the official package repository for Flutter and Dart packages:

dart pub global activate fvm
dart pub global activate melos

Configuration

To ensure accessibility of dart packages, it is necessary to add the directory containing the installed package binaries to the $PATH environment variable.

Begin by accessing the .zshrc file located in the $HOME directory:

open $HOME/.zshrc 

The second step involves appending the following two lines to the file and then saving it:

export PUB_CACHE="$HOME/.pub-cache"
export PATH="$PATH:$PUB_CACHE/bin"

Following the modification, restart the terminal to ensure that the changes made to the file are recognized. Alternatively we could refresh the environmental configuration by executing source $HOME/.zshrc.

You can verify the availability of packages by executing the commands where fvm and where melos, which should respectively display the paths of each executable.

Example workspace

For clarity, I will refer to the example workspace as “Foo Bar” throughout the remainder of this article.

The objectives of the example workspace are outlined as follows:

  • It encompasses a Flutter Android/iOS application named “Foo Bar Client”.
  • It incorporates 1 Dart package labeled “Foo Bar Shared”.
  • It includes a lints package titled “Foo Bar Lints”.
  • The shared package serves as a dependency for the client application.
  • The lints package acts as a dependency for both the client application and the shared package.
  • The Flutter version specific to the project is managed through FVM.
  • Melos oversees the management of the workspace.
  • The primary utilized code editor is VSCode.
  • Git is employed as the version control system.

Initialization of workspace

Initially, we need to create an empty directory named “foo_bar” to serve as the project workspace. This can be achieved by executing the command mkdir foo_bar.
Subsequently, we should proceed with creating the initial files in the root directory of the workspace.

foo_bar/pubspec.yaml

It serves as a vital component, housing fundamental information about the workspace alongside a comprehensive list of workspace dependencies.

name: foo_bar_workspace

publish_to: 'none'
version: 1.0.0

environment:
sdk: ">=3.4.0 <4.0.0"

dev_dependencies:
fvm: ^3.1.0
melos: ^6.0.0

foo_bar/.fvmrc

This file contains configurations related to FVM. It is essential to specify the Flutter version that our workspace will utilize.

{
"flutter": "3.22.0",
"flavors": {},
"runPubGetOnSdkChanges": false,
"updateVscodeSettings": false,
"updateGitIgnore": false
}

foo_bar/melos.yaml

Within this file lie configurations and scripts relevant to Melos. It is required to specify the location of our workspace packages, and optionally, define scripts that can be executed in the scope of the workspace.

name: foo_bar

sdkPath: .fvm/flutter_sdk

packages:
- app/**
- package/**

scripts:
analyze:
exec: dart analyze

get:
run: melos exec -c 1 -- "flutter pub get"

clean:
run: melos exec -c 1 -- "flutter clean"

The sdkPath property holds significance as it allows seamless integration of Melos with FVM within our workspace.

While it is possible to keep packages and applications within a single directory, it is advisable to distinct them for organizational purposes.

foo_bar/.vscode/settings.json

This file encompasses configurations specific to the VSCode editor.

{
"dart.flutterSdkPath": ".fvm/flutter_sdk",
"dart.runPubGetOnPubspecChanges": "never",
"files.watcherExclude": {
"**/.fvm": true
},
"search.exclude": {
"**/.fvm": true,
}
}

The dart.flutterSdkPath property holds significance as it enables seamless integration of VSCode with FVM within the scope of our workspace.

Furthermore, it is advisable to install the Flutter and Melos extensions for VSCode. Upon installation, it is recommended to restart VSCode for the changes to take effect.

foo_bar/.gitignore

This file contains specifications regarding directories and files that are not intended to be kept under version control.

.dart_tool
.fvm
.idea

*.iml

The subsequent step involves obtaining workspace dependencies by executing the following command at the root of the workspace:

dart pub get

Next, we should proceed to bootstrap the Melos workspace. This can be accomplished using the Melos VSCode extension and the VSCode Command Palette:

  • Open the command palette (CMD + Shift + P).
  • Search for the Melos: bootstrap option and select it.

Alternatively we could execute melos bootstrap in the terminal.

Following this, we should download the specified Flutter SDK for the workspace using FVM.

fvm use

To ensure the integrity of our project’s Flutter SDK, it is recommended to execute the command fvm flutter doctor in the terminal of VSCode.

Creating & linking workspace packages

In this section, we will proceed to create the packages outlined for the example workspace and link them together.

Lints package

This package is designated to house code style rules that will be uniformly enforced across all packages within the workspace.

To create the package, navigate to the foo_bar/package directory and initialise the package directory by executing mkdir lints .

Additionally, create the requisite files for our package as follows:

/// foo_bar/package/lints/pubspec.yaml

name: foo_bar_lints

publish_to: 'none'
version: 1.0.0

environment:
sdk: '>=3.4.0 <4.0.0'

dependencies:
flutter_lints: ^4.0.0
/// foo_bar/package/lints/lib/lints.yaml

include: package:flutter_lints/flutter.yaml

linter:
rules:
prefer_single_quotes: true
sort_constructors_first: true

analyzer:
errors:
unused_import: warning

Upon completing the file creation process, execute the Melos: boostrap command from the VSCode Command Palette.

Following this, proceed to execute the Melos: Run script -> get command as the subsequent step.

Shared package

This package is intended to contain shared code that can be reused across multiple applications or packages.

To begin, navigate to the foo_bar/package directory and establish a directory for the package by executing mkdir shared.

Subsequently, create the necessary source files for the package.

/// foo_bar/package/shared/lib/src/constant.dart

abstract class Constant {
static String foo = 'foo';
}
/// foo_bar/package/shared/lib/shared.dart

library shared;

export 'src/constant.dart';
/// foo_bar/package/shared/analysis_options.yaml

include: package:foo_bar_lints/lints.yaml
/// foo_bar/package/shared/pubspec.yaml

name: foo_bar_shared

publish_to: 'none'
version: 1.0.0

environment:
sdk: '>=3.4.0 <4.0.0'

dev_dependencies:
foo_bar_lints:
path: ../lints

After creating the files, execute the Melos: boostrapcommand from the VSCode Command Palette to link the foo_bar_lints local package dependency with the foo_bar_shared package.

Next, execute the Melos: run Run script -> get command.

Melos will track local package dependencies using pubspec_overrides.yaml files. These files should be kept under version control.

Client application

To create the client application, navigate to the foo_bar/app directory within the terminal of VSCode and execute the following command:

fvm flutter create --project-name client --org hu.tmenyhart.foobar --platforms android,ios client

Next, modify the analysis_options.yaml file to incorporate the lint rules from the foo_bar_lints package:

include: package:foo_bar_lints/lints.yaml

Additionally, replace the flutter_lints dependency with foo_bar_lints in the pubspec.yaml file:

dev_dependencies:
foo_bar_lints:
path: ../../package/lints

Fiinally, to add the foo_bar_shared package as dependency of the client application, modify the pubspec.yaml file accordingly:

dependencies:
foo_bar_shared:
path: ../../package/shared

Once the files are updated, execute the Melos: bootstrap command from the VSCode Command Palette to link the local package dependencies with the client application.

Following this, execute the Melos: Run script -> get command.

Melos offers the functionality to visualize the package dependency graph, particularly beneficial when working within larger workspaces with numerous packages. To utilize this feature, invoke the Melos: show package grap command.

Summary

While particularly advantageous for multi-application workspaces, it also proves beneficial for single-application workspaces by facilitating a modularized codebase structure and mitigating tight couplings between modules.

If you’re eager to kickstart your monorepo, you can access a GitHub project template here.

Simply create a new repository based on the template by selecting the Use this template -> Create a new repository option on Github.

--

--