Skip to content

About

Build Flutter apps for Apple Watch (watchOS): the Flutter commands you know, hot reload on the Simulator, DevTools, and release builds for the App Store.

Topics

Resources

Contributing

Stars

22 stars

Watchers

0 watching

Forks

Repository files navigation

flutter-watchos

A Flutter toolchain for building and running Flutter apps on Apple Watch (watchOS).

flutter-watchos is a CLI companion to the Flutter SDK that targets watchOS instead of iOS: the Flutter commands you know, hot reload on the Simulator, and DevTools.

Apple Silicon Mac only. Xcode is required. The watchOS engine tools are arm64-only, so an Intel Mac, or a terminal running under Rosetta, cannot build for the watch.

Accounts. The watchOS engine ships as pre-built binaries from flutterwatch.dev. The Simulator engine needs no account. Running on a physical watch and building for release need one — run flutter-watchos login: the page it opens signs you in with GitHub, and that creates the account. See Accounts & engine artifacts.

Current version

  • flutter-watchos: 0.1.1
  • Flutter SDK: 3.47.5 (6a19cca56475dbfba1478ee68d7bd0c2ef891da1)
  • watchOS engine artifacts: engine-779a2b7c61ba

Installation

Clone the repository and put its bin/ on your PATH. login connects your flutterwatch.dev account, which the Simulator does not need. precache downloads the watchOS engine, and doctor checks the setup.

git clone https://github.com/flutterwatch/flutter-watchos.git
cd flutter-watchos
export PATH="$PWD/bin:$PATH"
flutter-watchos login
flutter-watchos precache
flutter-watchos doctor

See Getting started for the full setup guide.

Usage

flutter-watchos substitutes the original flutter CLI command.

Check the installed tooling and list all connected devices:

flutter-watchos doctor -v
flutter-watchos devices

Create a new app project:

flutter-watchos create my_watch_app --platforms=watchos
cd my_watch_app

Build and run on a watchOS Simulator (debug, with hot reload and DevTools):

flutter-watchos run -d <simulator_id>

Build and run on a paired Apple Watch in profile mode (AOT, with logging and DevTools):

flutter-watchos run -d <watch_id> --profile

Or in release mode (AOT, and fastest):

flutter-watchos run -d <watch_id> --release
  • See Supported commands for all available commands and usage examples.
  • See Getting started to create your first app and try hot reload.
  • To update flutter-watchos to the latest released version, run flutter-watchos upgrade (use flutter-watchos upgrade --verify-only to just check).

Platform identity & limitations

flutter-watchos treats watchOS as its own platform at both the build and runtime layers. Read this section before adding dependencies to an existing iOS codebase — the separation has real consequences for plugins and cross-platform apps.

Runtime identity

On a watchOS build, the Dart VM reports:

API Value on watchOS Value on iOS
Platform.operatingSystem "watchos" "ios"
Platform.isIOS true true
Platform.isWatchOS true false
defaultTargetPlatform TargetPlatform.iOS TargetPlatform.iOS

Platform.isWatchOS exists only inside this toolchain — don't put it in shared code. The getter comes from our Dart VM patch, so it is real when we build the watch app, but it is absent from the stock Dart SDK. That has two consequences, and the second one is the serious one:

  • Your IDE reports The getter 'isWatchOS' isn't defined for the type 'Platform', because the analyzer resolves dart:io from the stock SDK even here.
  • Building the same file with regular Flutter fails outright — Error: Member not found: 'isWatchOS'. Not a warning; the iOS and Android builds stop. Any lib/ file shared with your iPhone or Android target must never name it.

Write FlutterWatchosPlatform.isWatch (from the flutter_watchos package) instead. It is a plain operatingSystem == "watchos" comparison, so it compiles under every toolchain and answers false off the watch. Reach for Platform.isWatchOS only in watch-only code that no stock Flutter build ever compiles.

Platform.isIOS is true on watchOS. Apple Watch runs the same Darwin kernel and Foundation as iPhone and iPad — it's part of the iOS family. Standard Flutter widgets that branch on Platform.isIOS or defaultTargetPlatform already render with iOS styling (Cupertino, SF font) on the watch, with no Flutter framework changes required.

The Flutter framework that flutter-watchos uses is unmodified. watchOS identity is contributed entirely by the Dart VM in our engine build and by the flutter-watchos CLI itself.

Plugin platform key

A Flutter plugin advertises which platforms it supports under flutter.plugin.platforms in its pubspec.yaml. Plugins target watchOS by adding a watchos: entry there:

flutter:
  plugin:
    platforms:
      watchos:
        ffiPlugin: true
        dartPluginClass: MyPluginWatchos
        ffiSymbols:
          - my_plugin_watchos_do_thing

A watchOS build only loads plugins that declare this key. Plugins targeting only ios: are not picked up — Apple Watch has a different surface (no WebKit, Digital Crown input, a tiny screen), so the safe default is to require explicit opt-in. watchOS plugins ship native code via dart:ffi; method-channel plugins are not supported (a pluginClass:-only entry builds, but its calls throw MissingPluginException — the tool warns about this).

The first-party flutter_watchos package adds the watch-specific APIs the framework doesn't cover: Digital Crown scrolling and raw input, Taptic Engine haptics, device info, and the system-clock toggle. A plugin that only implements iOS or macOS needs a watchOS implementation added under this key — see Using and writing watchOS plugins. flutter-watchos plugin port --from-pub <package> scaffolds that federated *_watchos package for you (details in plugin port), along with a report of how each API the plugin uses fares on watchOS — you supply the native implementation. Ready-made *_watchos packages live in flutterwatch/plugins.

Writing cross-platform apps (iOS + Android + watchOS)

If your app already targets iOS/Android and you're adding watchOS support, keep these patterns in mind:

1. Don't rely on Platform.isIOS alone for "phone/tablet iOS" logic. It's also true on Apple Watch. Refine with the flutter_watchos helpers:

import 'package:flutter_watchos/flutter_watchos.dart';

if (FlutterWatchosPlatform.isIos) {        // iPhone / iPad only (NOT watchOS)
  // Use iPhone-specific plugin
}

if (FlutterWatchosPlatform.isWatch) {      // Apple Watch only
  // compact, crown-driven UI
}

if (FlutterWatchosPlatform.isAppleMobile) {// iPhone, iPad, OR Apple Watch
  // Any iOS-family OS (Foundation present)
}

2. Design for the watch screen and the Digital Crown. Apps are small, scrollable, single-focus. The crown scrolls your lists the way it scrolls native ones with no code; WatchCrownScroll picks the list when a screen has several, and WatchCrown takes the crown as raw input for games and pickers. Handle the wrist going down with WatchAlwaysOnBuilder — watchOS keeps your app on screen, dimmed, so pause animations and hide private content. See the flutter_watchos README.

3. Plugin dependencies: if your iOS app uses url_launcher, shared_preferences, path_provider, etc., each one needs a watchOS federated package or your watch build will compile but calls will throw MissingPluginException at runtime. Audit your pubspec.yaml for plugins with native iOS code before porting.

4. ios/ and watchos/ directories are independent. flutter-watchos create scaffolds a watchos/ project with its own Info.plist and SwiftUI runner. Don't share it with ios/ — the build settings diverge (watchOS SDK, arm64-only).

Known limitations

  • Apple Watch Series 9 or later, Ultra 2 or later, or SE 3, on watchOS 26.0 or later for on-device runs. The engine is arm64-only; when WATCHOS_DEPLOYMENT_TARGET < 27.0 the executable needs an arm64_32 slice, so the template ships a stub slice and a "Requires Apple Watch Series 9 or later" fallback screen for older watches.
  • No debug (JIT) on a physical watch. The watchOS device SDK removes the Mach APIs the Dart JIT VM needs, so device-debug cannot even be built. Debug + hot reload run on the Simulator; a physical watch runs AOT (--profile for logging/DevTools, --release for shipping).
  • Profile on a physical watch. The Simulator does not reflect real on-device performance — always validate on an actual Apple Watch before shipping.
  • iOS plugins don't automatically work. Packages need a watchOS implementation (see the plugin key above); pure-Dart packages are unaffected.
  • No WebKit / webview_flutter. The watchOS SDK has no WebKit, so plugins depending on WKWebView will not compile and a page cannot be embedded in a Flutter layout. A page can still be shown full-screen: url_launcher with url_launcher_watchos 0.1.0 or later opens it in the system browser on the watch.
  • App Store submission needs an iOS container. create scaffolds a single independent watch app for build/run; wrapping it in an iOS companion archive for submission is handled separately — see Publishing.

Text input (the system keyboard), Digital Crown scrolling and haptics, and app-lifecycle events (WidgetsBindingObserver) are all supported.

Apps render with Impeller on Metal, the same renderer Flutter uses on iOS — no opt-in required. If a watch cannot open a Metal device the engine falls back to Skia's software rasterizer on its own, and an app can ask for that path explicitly with FLTEnableImpeller set to false in watchos/Runner/Info.plist.

Each frame reaches the screen as an image the engine hands to SwiftUI. An experimental zero-copy alternative shows the engine's render target directly through a SceneKit material (FlutterWatchOSPresent set to texture in Info.plist, or FLUTTER_WATCHOS_PRESENT=texture in the environment for a run). It covers frames with platform views too. It is off by default and untested with App Review; on a Series 10 it cut raster time by about two thirds for frames without platform views. Treat it as something to evaluate, not to ship.

Two more switches of the app's host help when measuring it, set in the environment for a run the same way:

  • FLUTTER_WATCHOS_DISPLAY_CLOCK=continuous keeps the display clock ticking while nothing on screen changes. By default it pauses after about half a second with nothing to draw, so a still app is not woken on every refresh.
  • FLUTTER_WATCHOS_CPU_LOG=<seconds> writes the app's CPU use, as a percentage, to the system log once per window of that many seconds (at least 1).

Docs

App development

Plugin development

Project internals

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md for details.

To run the tests:

flutter/bin/dart test test/general

License

BSD 3-Clause — see LICENSE.

This project incorporates code from Flutter, flutter-tizen and flutter-tvos (all BSD 3-Clause). The pre-built engine artifacts bundle the Flutter engine and Dart SDK; their aggregated open-source license ships as LICENSES.txt in each watchOS engine folder the CLI installs (engine_artifacts/watchos_*/), and covers the host SDK folders installed beside them. See THIRD_PARTY_LICENSES.md for full attribution.


flutter-watchos is an independent project and is not affiliated with, endorsed by, or sponsored by Google LLC or Apple Inc. Flutter and Dart are trademarks of Google LLC. Apple Watch and watchOS are trademarks of Apple Inc.

About

Build Flutter apps for Apple Watch (watchOS): the Flutter commands you know, hot reload on the Simulator, DevTools, and release builds for the App Store.

Topics

Resources

Contributing

Stars

22 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages