Install and Set Up Flutter: Your First App on Android, iOS, and Web

Flutter compiles one Dart codebase to Android, iOS, and web targets. Setup takes four steps: install the Flutter SDK, install each platform toolchain, confirm everything with flutter doctor, then run flutter create. The SDK install is quick. Most failures come from the Android SDK licenses, Xcode, or CocoaPods, so this guide covers those in detail. At the time of writing, the stable channel is at Flutter 3.44 with Dart 3.12.

Quick Takeaways

  • Install the SDK through the VS Code Flutter extension or manually, then verify with flutter doctor -v.
  • Android needs Android Studio, the SDK command-line tools, and accepted licenses. iOS needs macOS, Xcode, and CocoaPods. Web needs only Chrome.
  • One command, flutter create, scaffolds all three platforms. flutter run -d <device> launches any of them.
  • Run flutter doctor after every toolchain change. It reports what is missing.
Target Host OS Required Toolchain Run Command
Web Windows, macOS, Linux Chrome flutter run -d chrome
Android Windows, macOS, Linux Android Studio + SDK flutter run -d <emulator-id>
iOS macOS only Xcode + CocoaPods flutter run -d <simulator-id>

System Requirements

Flutter supports 64-bit Windows 10 and 11, macOS, and Linux. Plan for 10 GB or more of free disk space once Android Studio and its emulator images are installed. Xcode needs more.

Install these first:

  • Git: the SDK is a Git checkout, and flutter upgrade depends on it.
  • VS Code with the official Flutter extension, which also installs the Dart extension. Any Code OSS-based editor that supports VS Code extensions also works.
  • Google Chrome: the default web debug target.

On Linux, install the desktop build dependencies as well:

# Debian/Ubuntu: toolchain used by Flutter's native Linux builds
sudo apt-get install clang cmake git ninja-build pkg-config \
  libgtk-3-dev liblzma-dev libstdc++-12-dev

Never run Flutter commands with sudo. It creates permission problems in the SDK directory that are tedious to undo.

Install the Flutter SDK

There are two supported routes. Choose one.

Option 1: Install with the VS Code extension

This route downloads the SDK and adds it to your PATH for you.

  1. Open VS Code and install the Flutter extension from the Marketplace.
  2. Open the Command Palette (Ctrl/Cmd + Shift + P).
  3. Run Flutter: New Project. If no SDK is found, VS Code offers to download one.
  4. Pick an install folder such as ~/dev or C:\dev. Avoid paths with spaces and avoid C:\Program Files.
  5. Accept the prompt to add the SDK to PATH.
  6. Close and reopen all terminals, then restart VS Code.

Option 2: Install manually

Download the SDK archive from the Flutter site, extract it, and add its bin directory to PATH.

# macOS/Linux: add the SDK to PATH (zsh example)
echo 'export PATH="$HOME/dev/flutter/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

# Confirm the binary resolves
which flutter
flutter --version

On Windows, add %USERPROFILE%\dev\flutter\bin to the user Path environment variable. Move it to the top of the list so no older install shadows it.

flutter --version prints the framework version, the channel, and the bundled Dart version. The first run also downloads the engine artifacts, so expect a short delay.

Choose a channel

Channel Stability Use Case
stable Production-tested, quarterly releases Shipping apps (default)
beta Pre-release candidate Testing upcoming changes
main Unreviewed daily builds Contributing to the framework

Stay on stable unless you have a specific reason to switch. Many teams run one release behind the newest stable so package maintainers have time to catch up.

Verify with flutter doctor

flutter doctor -v

The -v flag prints paths, versions, and the exact fix for each failure. A fresh install looks like this:

[✓] Flutter (Channel stable, 3.44.0)
[!] Android toolchain - develop for Android devices
[!] Xcode - develop for iOS and macOS
[✓] Chrome - develop for the web
[✓] VS Code
[✓] Connected device (2 available)
[✓] Network resources

A [!] marks a fixable gap. A [✗] marks a blocker. Ignore Android or Xcode warnings until you configure those platforms. Re-run flutter doctor after each step below.

Configure Android

Android works on all three host operating systems.

  1. Install Android Studio.
  2. Open it and finish the setup wizard. It downloads the Android SDK and platform tools.
  3. Go to Settings → Languages & Frameworks → Android SDK → SDK Tools. Enable Android SDK Command-line Tools (latest).
  4. Accept the licenses:
# Prompts for each SDK license; answer "y" to all
flutter doctor --android-licenses
  1. Create a virtual device in Device Manager. Choose a recent Pixel profile and a current system image that matches your CPU architecture. Enable hardware acceleration in your BIOS if the emulator is slow.

List and start emulators from the terminal:

flutter emulators                    # list available AVDs
flutter emulators --launch Pixel_8   # boot one by id
flutter devices                      # confirm Flutter sees it

To use a physical phone, enable Developer options and USB debugging, connect it, and approve the RSA fingerprint prompt. It then appears in flutter devices.

If flutter doctor reports cmdline-tools component is missing, step 3 is the cause. If it reports an unknown Java or SDK location, point Flutter at the right one:

flutter config --android-sdk "/path/to/Android/sdk"

Configure iOS

iOS builds require a Mac. Windows and Linux cannot compile for iOS or run the Simulator.

  1. Install Xcode from the Mac App Store.
  2. Select it as the active developer directory and accept the license:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
sudo xcodebuild -runFirstLaunch
  1. Install the iOS platform runtime when Xcode prompts for it, or add it under Xcode → Settings → Components.
  2. Install CocoaPods, which Flutter uses to manage native iOS dependencies:
brew install cocoapods
  1. Open the Simulator:
open -a Simulator
flutter devices   # the simulator should now be listed

Running on a physical iPhone also requires a free Apple ID added under Xcode → Settings → Accounts. Set a signing team on the Runner target before the first device build. App Store distribution requires a paid Apple Developer Program membership.

Configure Web

Web needs nothing extra beyond Chrome. Confirm Flutter sees it:

flutter devices
# Chrome (web) • chrome • web-javascript • Google Chrome ...

If Chrome sits in a non-standard location, set the CHROME_EXECUTABLE environment variable to its path.

Create Your First App

Scaffold the project and target all three platforms explicitly:

flutter create --platforms=android,ios,web my_first_app
cd my_first_app

flutter create generates this structure:

Path Purpose
lib/main.dart App entry point and your Dart code
pubspec.yaml Dependencies, assets, and SDK constraints
android/ Gradle project for Android builds
ios/ Xcode project for iOS builds
web/ index.html and web manifest
test/ Widget tests

Directories only appear for platforms you selected. Add another later with flutter create --platforms=web . from inside the project.

Replace the starter code

The default counter demo works, but a minimal app shows the core model more clearly. Replace lib/main.dart with:

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

/// Root widget: configures theme and the home screen.
class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My First Flutter App',
      theme: ThemeData(
        colorSchemeSeed: Colors.indigo,
        useMaterial3: true,
      ),
      home: const CounterPage(),
    );
  }
}

/// Stateful widget: owns a mutable counter.
class CounterPage extends StatefulWidget {
  const CounterPage({super.key});

  @override
  State<CounterPage> createState() => _CounterPageState();
}

class _CounterPageState extends State<CounterPage> {
  int _count = 0;

  // setState() schedules a rebuild so the UI reflects the new value.
  void _increment() => setState(() => _count++);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Hello, Flutter')),
      body: Center(
        child: Text(
          'Taps: $_count',
          style: Theme.of(context).textTheme.headlineMedium,
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _increment,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

The app shows a centered label and a floating button. Each tap calls setState(), which marks the widget dirty. Flutter then re-runs build() and redraws only what changed. StatelessWidget holds no mutable data. StatefulWidget keeps its data in a separate State object that survives rebuilds.

Run on Android, iOS, and Web

List every available target, then pass the id with -d:

flutter devices
flutter run -d chrome          # web
flutter run -d emulator-5554   # Android emulator
flutter run -d "iPhone 16"     # iOS Simulator (macOS only)

The first Android build downloads Gradle and its dependencies. Expect several minutes. Later builds are much faster.

While flutter run is active, the terminal accepts these keys:

Key Action Preserves State
r Hot reload: injects changed code Yes
R Hot restart: restarts the app No
q Quit n/a

Change 'Taps: $_count' to another string and press r. The UI updates in about a second and the counter keeps its value. Hot reload does not re-run initState() or changed global initializers, so use a hot restart for those.

Build release artifacts

flutter build web --release   # output: build/web/
flutter build apk --release   # output: build/app/outputs/flutter-apk/
flutter build appbundle       # Play Store upload format (.aab)
flutter build ipa             # iOS archive (macOS, signing required)

The build/web/ folder is static. Host it on any static file server.

Common Setup Errors and Fixes

Symptom Cause Fix
Unable to locate Android SDK SDK path not set flutter config --android-sdk <path>
Android license status unknown Licenses not accepted flutter doctor --android-licenses
cmdline-tools component is missing Tools not installed Enable in SDK Manager → SDK Tools
CocoaPods not installed Missing dependency brew install cocoapods
flutter: command not found PATH not updated Add flutter/bin to PATH, reopen terminal
Emulator never appears Acceleration disabled Enable virtualization in BIOS/UEFI

Best Practices and Refactoring

Early Flutter projects tend to collect the same mistakes. Two of the most common are below.

Anti-pattern: one giant build method and unscoped rebuilds

// BAD: a single huge build() that rebuilds everything on every tap
class _BadPageState extends State<BadPage> {
  int _count = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          // Static header rebuilt on every setState
          Container(
            padding: EdgeInsets.all(16),
            child: Text('Dashboard', style: TextStyle(fontSize: 24)),
          ),
          Text('$_count'),
          ElevatedButton(
            onPressed: () => setState(() => _count++),
            child: Text('Add'),
          ),
        ],
      ),
    );
  }
}

This version gets four things wrong. It omits const on widgets that never change. It places static UI inside a rebuilt tree. It hard-codes a text style instead of reading the theme. It also mixes layout and state in one method.

Refactored: extracted widgets and const constructors

// GOOD: static parts are const widgets Flutter can skip on rebuild
class _GoodPageState extends State<GoodPage> {
  int _count = 0;

  void _increment() => setState(() => _count++);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          const _Header(),                  // never rebuilds
          _CountLabel(count: _count),       // rebuilds only when count changes
          FilledButton(
            onPressed: _increment,
            child: const Text('Add'),
          ),
        ],
      ),
    );
  }
}

class _Header extends StatelessWidget {
  const _Header();

  @override
  Widget build(BuildContext context) => Padding(
        padding: const EdgeInsets.all(16),
        child: Text(
          'Dashboard',
          style: Theme.of(context).textTheme.headlineSmall,
        ),
      );
}

class _CountLabel extends StatelessWidget {
  const _CountLabel({required this.count});
  final int count;

  @override
  Widget build(BuildContext context) => Text('$count');
}

A const widget is built once at compile time, so Flutter can skip it during a rebuild. Extracted widgets keep each build() short and testable. Reading styles from Theme.of(context) keeps light and dark modes consistent.

Habits worth keeping from day one

  • Run flutter analyze before each commit. It catches missing const, unused imports, and unsafe BuildContext use across async gaps.
  • Run dart format . to keep formatting uniform.
  • Commit pubspec.lock for apps. Do not commit it for published packages.
  • Run flutter pub outdated regularly, and flutter upgrade only between feature branches.
  • Pin the SDK version in CI so local and build-server results match.

Keep the SDK Updated

flutter upgrade          # move to the newest release on your channel
flutter channel stable   # confirm or switch channel
flutter clean            # delete build/ and .dart_tool/ when builds act strangely
flutter pub get          # re-resolve dependencies after pulling changes

After a major upgrade, run flutter doctor again and rebuild each platform once. Native dependency versions, such as Gradle and CocoaPods, sometimes change between releases.

FAQ

How do I install Flutter on Windows, macOS, or Linux?

Install Git and VS Code, add the Flutter extension, and run Flutter: New Project from the Command Palette. VS Code downloads the SDK and offers to add it to PATH. You can also extract the SDK archive manually and add its bin folder to PATH. Verify with flutter doctor -v.

Can I build an iOS app on Windows or Linux?

No. iOS builds need Xcode, which runs only on macOS. On Windows or Linux you can develop for Android and web. To ship an iOS app, build on a Mac or use a macOS-based CI service.

What does flutter doctor check?

It checks the Flutter SDK, the Android toolchain and licenses, Xcode and CocoaPods on macOS, Chrome for web, your IDE plugins, connected devices, and network access. Run flutter doctor -v for detailed paths and fixes.

Do I need to know Dart before learning Flutter?

No. If you know Java, Kotlin, Swift, C#, or TypeScript, you can read Dart quickly. Learn null safety, async/await, and Future and Stream early, since Flutter code uses them constantly.

Hot this week

Humanoids in Factories and Warehouses: What They Actually Do Today

Which humanoid robots do real work in factories and warehouses today? See deployment data, ROI math and ROS 2 code. Read the 2026 guide.

Debugging Robot Code: Logs, Serial Output, rosbag, and Foxglove

Debug robot code faster with ROS 2 logging, serial capture, rosbag2 recording, and Foxglove. Copy the configs and scripts into your stack today.

Build a Mapping Robot With SLAM From Scratch

Build a SLAM mapping robot from scratch: wiring, odometry, LiDAR, ROS 2 slam_toolbox config and tuning. Follow the full guide and map your first room.

Depth Cameras: Stereo, Time-of-Flight, and Structured Light Compared

Compare stereo, time-of-flight, and structured light depth cameras: accuracy, range, sunlight, ROS2 setup. Pick the right sensor for your robot.

Python vs C++ for Robotics: Which Should You Use?

Python vs C++ for robotics compared: latency, ROS2 performance, control loops, and code examples. Learn which language fits your robot. Read the guide.

Topics

Humanoids in Factories and Warehouses: What They Actually Do Today

Which humanoid robots do real work in factories and warehouses today? See deployment data, ROI math and ROS 2 code. Read the 2026 guide.

Debugging Robot Code: Logs, Serial Output, rosbag, and Foxglove

Debug robot code faster with ROS 2 logging, serial capture, rosbag2 recording, and Foxglove. Copy the configs and scripts into your stack today.

Build a Mapping Robot With SLAM From Scratch

Build a SLAM mapping robot from scratch: wiring, odometry, LiDAR, ROS 2 slam_toolbox config and tuning. Follow the full guide and map your first room.

Depth Cameras: Stereo, Time-of-Flight, and Structured Light Compared

Compare stereo, time-of-flight, and structured light depth cameras: accuracy, range, sunlight, ROS2 setup. Pick the right sensor for your robot.

Python vs C++ for Robotics: Which Should You Use?

Python vs C++ for robotics compared: latency, ROS2 performance, control loops, and code examples. Learn which language fits your robot. Read the guide.

Build a ROS 2 Rover: Raspberry Pi 5, Motors, and LiDAR

Build a ROS 2 rover with Raspberry Pi 5, RPLIDAR C1, and a Pico motor controller. Copy the code, wire it, and map your room. Start building now.

Imitation Learning for Robots: Teach by Demonstration

Learn imitation learning for robots: behavior cloning, DAgger, ACT, and diffusion policies with ROS2 code. Build your first policy today.

LiDAR for Robots: 2D, 3D, and Solid-State Compared

Compare 2D, 3D, and solid-state LiDAR for robots. See specs, ROS2 configs, code, and a selection workflow. Pick the right sensor now.

Related Articles

Popular Categories