Flutter: guide for using FVM with Melos
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: boostrap
command 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.