Setting Up Your Flutter Environment, the Right Way — Part 1
After more than a decade of using macOS (and its predecessor, OS X), I admit I’ve been spoiled. As a full stack developer, I’ve gotten used to nice things — including Homebrew, which allows for both automated installation of many packages and version management.
So I was surprised when I started working more closely with Flutter and found out in its official documentation that there’s lack of support for Homebrew. Actually, to be more precise, there’s Flutter in Homebrew formulas, but you can’t install any versions other than the latest one. However, it’s often necessary to keep older versions of Flutter for testing or for reproducing issues.
Difficulties
That said, downloading each version and remembering where I put it locally wasn’t necessarily easy for me. As a result, I ran into problems pretty quickly. To make things worse, Android Studio was installed on my computer via the JetBrains Toolbox app, which meant frequent updates took place in the background. So often, I had multiple versions of Android Studio, which suffice to say, I also needed for referencing Flutter’s local path.
I told myself there must be a better way. And then, I discovered Flutter Version Management (FVM), which helped me get my Flutter affairs under control.
How It Works
FVM is basically a native package manager, but for Flutter. Another similar tool you may recognize by using fvm
is Ruby Version Manager (RVM). In fact, if you’re familiar with RVM, you’ll probably recognize some of the FVM commands.
Installing FVM
The next section will outline how to install FVM.
macOS / Linux
Load the FVM repository into your Homebrew:
brew tap leoafarias/fvm
Install FVM:
brew install fvm
Windows
On Windows, run the following:
choco install fvm
There are other ways to install FVM locally, but what’s outlined above is good enough to start with.
Using FVM
Using FVM is almost as easy as installing it, but let’s first go over the Flutter lifecycle.
-
Install Flutter (the latest one or a specific version):
#Usage: fvm install - # Installs version found in the project configuration. fvm install {version} - # Installs a specific version. #Option: # -h, --help Print this usage information. # -s, --skip-setup Skips Flutter setup after install.
If you want to check the currently available versions, use the following command:
#Usage:
fvm releases
Some of the shortcut versions can be:
-
stable
— The latest production-stable version, in the Stable channel section of Flutter SDK releases. -
beta
— The latest prerelease version, in the Beta channel section of Flutter SDK releases.
The above information is explained in detail here: Flutter build release channels.
-
Switch to the active version (globally, or inside the local project directory):
#Usage: fvm use {version} #Or, if you need it globally: fvm global {version} #Option: # -h, --help Print this usage information. # -f, --force Skips Flutter project checks. # -p, --pin Pins the latest release channel instead of the channel itself. # --flavor Sets the version for a project flavor.
-
List the installed Flutter versions (those currently present on your local machine):
#Usage: fvm list #Option: # -h, --help Print this usage information.
-
Remove or uninstall a specific Flutter version:
Usage: fvm remove {version} Option: -h, --help Print this usage information. --force Skips version global check.
The globally set Flutter version requires adding FVM’s bin folder path to .profile
or .zshrc
. FVM will offer you the exact path after executing fvm global {version}
, but, for example, I have it set like this:
export PATH="$PATH:$HOME/fvm/default/bin"
Flutter Flavors
Sometimes, your product or development process will require multiple environments and configurations, like staging
, production
, dev
, and ci
. Each of these usually requires a different configuration, different API endpoints, different authentication tokens, etc. So having them predefined and easily distinguishable from each other speeds up development and prevents potential mistakes.
We’ll cover the full process in detail in the second part of this series.
In the meantime, here’s an example of script commands you can use to address different environments:
# Flavor usage example # Running flavors on DEBUG mode: flutter run –flavor dev -t lib/main_dev.dart flutter run –flavor prod -t lib/main_production.dart # Running flavors on RELEASE mode: flutter run –release –flavor dev -t lib/main_dev.dart flutter run –release –flavor prod -t lib/main_production.dart
As an example, you’ll need to change the AppConfig
class — with configuration sets for the development (dev
) and production (prod
) — separately:
import 'package:flutter/material.dart'; // 1 enum Environment { dev, prod } // 2 class AppConfig extends InheritedWidget { // 3 final Environment environment; final String appTitle; // 4 const AppConfig({ Key? key, required Widget child, required this.environment, required this.appTitle, }) : super( key: key, child: child, ); // 5 static AppConfig of(BuildContext context) { return context.dependOnInheritedWidgetOfExactType<AppConfig>()!; } // 6 @override bool updateShouldNotify(covariant InheritedWidget oldWidget) => false; }
You can read more details in the documentation.
ℹ️ Note: The example above isn’t a fully complete and working example, so you’ll need to complete a few more additional steps, which will be covered in part 2 of this series. In the meantime, please refer to the Flutter flavors documentation.
Android Studio Challenges
Now, let’s return to my original difficulty with multiple Android Studio and JetBrains Toolbox installations on my computer.
After installing Flutter (in any of the ways, including downloading directly from the link), I had an issue after running the flutter doctor
command. I’d get the same error every time:
*[!] Android Studio ✗ Unable to find bundled Java version.*
TLDR
It turns out this error isn’t related to your Flutter installation, no matter how it was installed, but rather to the missing folder under the Android Studio installation folder.
Solution
In short, to fix this, you’ll need to add a symlink reference to a JDK folder path installed locally on your machine:
-
Go to the Android Studio installation folder:
cd ~/Applications/Android\ Studio/ # Or, if it's installed via JetBrains Toolbox cd ~/Library/Application\ Support/JetBrains/Toolbox/apps/AndroidStudio/{version} # On my machine, the current version is `AndroidStudio/ch-1/213.7172.25.2211.8624637`.
-
After finding a folder with a specific version, navigate to the
Contents > jre
folder:
cd Android\ Studio\ Preview.app/Contents/jre
-
Symlink your current
Java jdk
folder to thejdk
folder insideAndroid Studio/Contents/jre
:
ln -s /Library/Java/JavaVirtualMachines/{jdk-version} jdk
# Example:
ln -s /Library/Java/JavaVirtualMachines/zulu-11.jdk jdk
You can check your Java_Home
environment variable by using:
/usr/libexec/java_home
Conclusion
FVM can provide a really neat experience managing your Flutter versions without the need to remember the folder or path where you downloaded and installed things locally. Moreover, FVM tools can also be used in an automated CI pipeline and provide a more elegant way of handling downloading, installing, and upgrading Flutter versions.
I hope you’ll find it practical as well, and that your Flutter local version management headache will be a little bit more bearable.
See you soon in part 2, where I’ll explain the rest of the configuration related to Flutter flavors and different configuration environments.