skip to content

With two client Flutter apps needing different SDK versions on one laptop, how does FVM keep each project on its own version?

level: juniorimportance: must knowfreq 55%

answer

  1. one global flutter is the problem
  2. many SDKs in one cache
  3. fvm use writes .fvmrc
  4. commit .fvmrc, ignore .fvm/
  5. fvm flutter reads the pin

basics

~20 s

FVM keeps several Flutter SDKs in a shared cache and records each project's version in a committed .fvmrc file; fvm flutter and fvm dart then run the SDK that project pins, so the two apps never share one global Flutter.

solid answer

~40 s

A single global Flutter SDK forces every project onto one version, so the other client either breaks or someone keeps switching channels. FVM installs several SDKs side by side in its cache, `~/fvm/versions` by default. In each project, `fvm use 3.44.3` writes the pin to `.fvmrc`, creates `.fvm/flutter_sdk` pointing at that cached SDK, adds `.fvm/` to `.gitignore`, and runs `flutter pub get` with the new SDK. Afterwards `fvm flutter run` or `fvm dart format` inside project A uses A's SDK, and the same commands in project B use B's. The `.fvmrc` file is committed, so teammates and CI get the same version with `fvm install`; plain `flutter` still resolves through `PATH`, which is why teams either prefix commands with `fvm` or point the IDE at the project's SDK.

code

bash · 8 lines
bash
cd ~/work/client_a && fvm use 3.41.4   # writes .fvmrc, links .fvm/flutter_sdk, runs pub get
cd ~/work/client_b && fvm use 3.47.5

cd ~/work/client_a && fvm flutter --version   # 3.41.4
cd ~/work/client_b && fvm flutter --version   # 3.47.5

# a teammate after cloning client_a
fvm install                                  # reads .fvmrc, installs 3.41.4, recreates links

go deeper

for a junior

Know why one global SDK causes trouble across projects, that fvm use writes .fvmrc, and that fvm flutter runs the pinned version.

for a middle

Explain the shared cache, the per-project link, what to commit versus ignore, and why plain flutter still follows PATH.

for a senior

Make pins reproducible for the whole team and CI: exact versions, fvm install after cloning, and IDEs pointed at the project SDK.

for a principal

Decide when a team adopts per-project pinning, and how SDK upgrades become reviewed changes to .fvmrc instead of machine drift.

## The problem FVM solves The Flutter SDK is normally installed once and put on `PATH`. Every project on the machine then builds with that one version. That breaks down quickly: - **Client A** is on an older stable release, and upgrading it is a planned project of its own. - **Client B** needs a feature from the current stable. - Switching the global SDK back and forth means re-downloading artifacts, regenerated lock files and, worst of all, builds that quietly differ from what CI and teammates produce. An SDK mismatch is the classic cause of works-on-my-machine in Flutter: a different Flutter version brings a different Dart version, different framework APIs and different lints. ## How FVM works **FVM (Flutter Version Management)** is a command-line tool that separates *which SDKs are installed* from *which SDK a project uses*. 1. **A shared cache.** SDKs are cloned into `~/fvm/versions/<version>` by default (`FVM_CACHE_PATH` moves it). Each version is installed once and reused by every project that pins it. 2. **A per-project pin.** `fvm use <version>` records the version in **`.fvmrc`** at the project root. 3. **Links into the cache.** The same command creates **`.fvm/flutter_sdk`**, a link to the cached SDK, which IDEs and tools can point at. 4. **A proxy.** `fvm flutter ...` and `fvm dart ...` look up the nearest `.fvmrc`, make sure that SDK is cached, and run it with its `bin` directories first on `PATH`. ## Setting up the two-client scenario | Step | Client A | Client B | |---|---|---| | Pin | `fvm use 3.41.4` | `fvm use 3.47.5` | | Written | `.fvmrc` with `"flutter": "3.41.4"` | `.fvmrc` with `"flutter": "3.47.5"` | | Run | `fvm flutter run -d <device>` | `fvm flutter run -d <device>` | | Result | builds with 3.41.4 and its Dart | builds with 3.47.5 and Dart 3.13 | By default `fvm use` also downloads the SDK's own dependencies (`--skip-setup` skips that), runs `flutter pub get` with the new SDK (`--skip-pub-get` skips that), and updates VS Code's SDK setting. If the SDK's Dart version does not satisfy the `environment: sdk:` constraint in `pubspec.yaml`, it warns and asks before continuing. ## What to commit - **Commit `.fvmrc`.** It is the shared record of the project's Flutter version. - **Ignore `.fvm/`.** It holds machine-specific links into the local cache; FVM adds `.fvm/` to `.gitignore` automatically when `updateGitIgnore` is on (the default). A teammate who clones the repository runs `fvm install` with no argument: FVM reads `.fvmrc`, installs that version if needed and recreates the links. ## The catch: plain flutter `fvm` does not replace the `flutter` on your `PATH`. Typing `flutter run` in project A still runs whatever `flutter` is first on `PATH` — possibly B's version or a global one. Three ways teams handle it: - always type `fvm flutter` and `fvm dart`; - let the IDE use the project's SDK (FVM sets VS Code's `dart.flutterSdkPath`; Android Studio is pointed at `.fvm/flutter_sdk` by hand); - use `fvm global` for a default and accept that the per-project pin needs the `fvm` prefix. ## When FVM is worth it It pays off whenever more than one Flutter project lives on a machine, when CI must match developers exactly, or when upgrading Flutter is a deliberate, reviewed change. For a single hobby app on the latest stable, a plain global install is enough.

  • Why does typing flutter instead of fvm flutter inside a pinned project sometimes use the wrong version?
    FVM does not intercept the `flutter` command. Plain `flutter` resolves through `PATH`, which may point at a global SDK or another version. Only `fvm flutter`, `fvm dart`, an IDE configured with the project's SDK, or a `PATH` pointing at `.fvm/flutter_sdk/bin` uses the pin.
  • What happens when fvm use picks an SDK whose Dart version does not satisfy the pubspec's environment constraint?
    FVM compares the cached SDK's Dart version with the `environment: sdk:` constraint in `pubspec.yaml`. If it is not allowed, it reports the mismatch and asks whether to proceed, defaulting to no; `--force` skips the confirmation. Proceeding usually means `pub get` fails or the code uses unavailable language features.

FVM is a tool wall with several labelled wrenches: each project's work order names the wrench it needs, instead of everyone sharing whichever single wrench happens to be on the bench.

saying these in an interview costs you the question

  • Switches the global Flutter channel every time the project changes
  • Commits the .fvm directory with its links into the local cache
  • Assumes plain flutter automatically follows the project's .fvmrc
  • Thinks FVM downloads a separate full SDK copy into every project
  • Believes pinning a version also pins every package version