ModernCS
Session 1.190 minFree preview

Setup That Actually Finishes

Run flutter doctor to green, boot an emulator or simulator, and know what to do when the Android toolchain refuses.

By the end of this session you will be able to:

  • Read a flutter doctor -v report and say which lines block a build and which you can ignore today.
  • Get an Android emulator or an iOS simulator to appear in flutter devices, driven entirely from the terminal.
  • Create a project, run it on that device, and hot reload it without opening an IDE.

Everything here is Flutter 3.44, which bundles Dart 3.12. You do not install Dart separately. flutter --version prints both, and if it prints a Dart version that did not come with your Flutter SDK, you have two Darts on your PATH and that is your first bug.

What flutter doctor is actually checking

flutter doctor is not a health score. It is a list of independent toolchains, and each one only matters if you intend to build for that target. Run the verbose form, always:

flutter doctor -v

The short form gives you one line of verdict per section. The verbose form gives you the directories it searched, the versions it found, and the exact command it wants you to run next. Most setup problems are readable straight off the -v output and completely invisible in the short one.

Each section opens with a bracket. A check mark means that toolchain is complete. [!] means Flutter found the toolchain but something inside it is missing or unverified. [X] means it did not find the toolchain at all.

Here is the part people get wrong: a [!] or an [X] on a platform you are not building for is not a problem. A student on Windows or Linux will have [X] Xcode forever, and it will never once stop them from shipping an Android app. Chasing green on every line is how a two hour setup becomes a two day setup.

So decide your target before you fix anything. On a Mac you can do both, and you should still get Android working first, because Android is the one that breaks.

A report worth acting on looks like this:

Doctor summary (to see all details, run flutter doctor -v):
[!] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
    X cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
    X Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
[!] Android Studio (version 2025.2)
    X Unable to find bundled Java version.
[X] Xcode - develop for iOS and macOS
    X Xcode installation is incomplete.

Three complaints, and on a Windows machine only two of them are real.

The Android toolchain, in the order that works

Those two Android lines are one problem, and the order you fix them in decides whether you spend ten minutes or an afternoon.

flutter doctor --android-licenses does not accept licenses itself. It shells out to sdkmanager, and sdkmanager lives inside the Android SDK Command-line Tools package. If that package is not installed, the licenses command fails with Android sdkmanager tool not found, which reads like a Flutter bug and is not one. Install the tools first, accept the licenses second.

Open Android Studio, then Tools > SDK Manager:

  1. On the SDK Platforms tab, select API Level 36.
  2. On the SDK Tools tab, select Android SDK Command-line Tools, Android SDK Build-Tools, Android SDK Platform-Tools, and Android Emulator.
  3. Click Apply and let it download.

Then, and only then:

flutter doctor --android-licenses

Read each license, type y, press Enter. Re-run flutter doctor -v, and the Android toolchain section should go green.

The Unable to find bundled Java version line is separate. Flutter looks for a JDK in three places, in order: the one bundled with Android Studio, then JAVA_HOME, then whatever java is on your PATH. Install Android Studio somewhere non-standard, or through JetBrains Toolbox, and step one fails while the others may find the wrong version. Tell Flutter where to look instead of guessing:

flutter config --android-studio-dir="/Applications/Android Studio.app"
flutter config --jdk-dir="/Applications/Android Studio.app/Contents/jbr/Contents/Home"

Those settings persist. flutter config --list shows what you have set, worth checking before you believe any error about a missing SDK.

A device has to exist before a build can land

flutter run needs a target. If nothing is attached, it will tell you so and stop.

On Android, ask what emulators are already defined:

flutter emulators

If that list is empty, Flutter will make you one based on a Pixel device:

flutter emulators --create --name webcraft
flutter emulators --launch webcraft

If --create reports no system image, install one for API 36 in the SDK Manager. Add --cold when an emulator boots into a corrupted saved state, which happens after a hard shutdown:

flutter emulators --launch webcraft --cold

On a Mac, the iOS simulator needs Xcode fully initialized once. These four commands, in order:

sudo sh -c 'xcode-select -s /Applications/Xcode.app/Contents/Developer && xcodebuild -runFirstLaunch'
sudo xcodebuild -license
xcodebuild -downloadPlatform iOS
open -a Simulator

The third one downloads the iOS runtime and is several gigabytes. Skipping it is why flutter doctor says an Xcode installation is incomplete while Xcode itself opens fine.

Either way, confirm before you build:

flutter devices

You want at least one line with a device ID in it. That ID is what -d takes.

The first run

flutter create first_build
cd first_build
flutter run

If more than one device is connected, flutter run prompts. Pass -d with an ID from flutter devices to skip the prompt.

The first build is slow, and this is where students give up. On Android, Gradle downloads its distribution and the dependency graph. On iOS, Flutter 3.44 resolves Swift packages, because Swift Package Manager replaced CocoaPods as the default dependency manager in this release. Ten minutes with no visible progress is normal exactly once. The second build takes seconds.

When it lands, the terminal prints its key commands. Three matter:

  • r hot reload: recompiles changed code and injects it into the running app, keeping state.
  • R hot restart: rebuilds the app from scratch, discarding state.
  • q quit.

Open lib/main.dart, find the string 'Flutter Demo Home Page', change it to something else, save, and press r in the terminal. The title on the device changes in under a second and the counter on screen keeps its value. That is your toolchain wired end to end.

Try it

  1. Run flutter doctor -v and write down, for your operating system, which sections you actually need. Ignore the rest on purpose.
  2. Fix your target platform's sections until they are green, cmdline-tools before licenses.
  3. Get one device listed by flutter devices.
  4. flutter create first_build, cd first_build, flutter run.
  5. Change the home page title string, save, press r.

You are done when the app is running on your device, the title reflects your edit, the counter did not reset, and you can name one section of flutter doctor that is still not green and say why that is fine.

Common mistakes

  • Trying to make every doctor line green. The report covers every platform Flutter supports, not the one you chose. Fix your target, note the rest, move on.
  • Running flutter doctor --android-licenses first. It needs sdkmanager, which ships in the Command-line Tools package. Install that from the SDK Manager, then accept licenses.
  • Killing the first build because it looks hung. Gradle and Swift Package Manager both fetch a lot on run one. Let it finish once. If you interrupt it halfway you get a corrupted cache and a genuinely broken build.
  • Unzipping the SDK into a path with spaces, non-ASCII characters, or admin-only write permission. flutter upgrade and Gradle both write inside that directory, and both fail in ways whose error messages never mention the path.
  • Pasting a fix written for an older SDK layout. Android's SDK directory structure and the Gradle plugin have both moved recently. Prefer the command flutter doctor -v printed at you over one a stranger typed in 2022.

Where this goes next

Your toolchain builds and hot reloads, so from here the bottleneck is the language rather than the tooling. The next session, Dart, Typed and Null-Safe, puts real types on variables and functions and works out why String? is a different type from String, before any of it gets buried under a widget tree.

That was one session of 5 in this phase.

Mobile Development runs to 4 phases. Buy the whole course, or just the phase you need.