Skip to content
Blog

Flutter Release Channels, Breaking Changes, and the Compatibility Policy

What the stable, beta, and main channels guarantee, how Flutter proposes, deprecates, and ships breaking changes, and how a team should pin and upgrade safely.

Published on • October 4, 2026

AI Assistant

Flutter Release Channels, Breaking Changes, and the Compatibility Policy

Every Flutter team eventually hits the same wall: a colleague upgrades the SDK over the weekend, CI goes red on Monday, and half the team is now compiling code the other half cannot build. The root cause is rarely a bug — it is a misunderstanding of which channel you are on, what that channel promises, and how the Flutter team decides it is allowed to change an API you already ship against.

Flutter publishes an explicit contract for this: three release channels with different guarantees, a compatibility policy built on a registry of contributed tests, a documented process for landing breaking changes, and release notes that say exactly what changed and how to migrate. Reading them well turns an SDK upgrade from a fire drill into a scheduled chore.

Key technologies: Flutter release channels (stable, beta, main), the flutter/tests registry, the breaking-change index and migration guides, dart fix, the flutter-announce and Dart announce mailing lists, pubspec.lock, and the Flutter SDK archive release calendar.

The Three Release Channels

The Flutter SDK archive ships builds for three channels. Each has a distinct promise:

ChannelWhat it containsGuaranteeWho should use it
StableThe most stable builds; roughly every third beta is promoted to stable.Recommended for new users and production app releases.Everyone shipping to users.
BetaThe most recent Flutter, not yet stable. Usually released on the first Wednesday of the month; a fix reaches beta about two weeks after landing on main.Not stable; expected to change.Teams validating an upcoming release.
MainThe newest features, not fully tested, possibly buggy. Previously called the master channel. No installation bundles — you clone the repo.None. Not recommended unless you are contributing to Flutter itself.Flutter contributors.

Two practical details are easy to miss. First, main is the current name of what older blog posts call master; installation bundles are only published for stable and beta, so main means cloning flutter/flutter from GitHub and letting the tool download SDK dependencies. Second, Flutter version numbers follow a modified calendar versioning scheme called CalVer, which is why versions look like 3.47.0 rather than semantic-versioned jumps.

The Release Calendar Is Public

Predictability is a design goal, not a hope. Flutter publishes release windows with branch cutoff dates — the deadline for a pull request to land on master (Flutter) or main (Dart) to guarantee inclusion in the next stable release. The 2026 schedule published in the SDK archive:

Flutter versionRelease targetBranch cutoff date
Flutter 3.41February 20262026-01-06
Flutter 3.44May 20262026-04-07
Flutter 3.47August 20262026-07-07
Flutter 3.50November 20262026-10-06

Before the cutoff your PR ships in the next stable; after it, it waits for the following cycle. The 2026 roadmap sets the volume: a minimum of four stable releases for both Dart and Flutter and 12 beta releases in 2026, with more release automation. The roadmap calls itself aspirational — treat the calendar as firm and the feature list as soft.

What the Compatibility Policy Actually Guarantees

The compatibility policy is built around a test registry: the flutter/tests repository, where you submit unit tests for your own applications or libraries. Flutter runs those tests on every change. The commitment is explicit — the team will not make a change that breaks these tests without working with the authors of those tests to (a) determine whether the change is sufficiently valuable and (b) provide fixes so the tests continue to pass.

That gives “breaking change” a precise definition: a change that caused one or more of these submitted tests to require changes. It is not a matter of opinion or of how many developers complained. The customer_testing shard runs on flutter/flutter pull requests, and Google additionally runs tens of thousands of proprietary tests on each commit. There are no exemptions, because failing these tests closes the tree.

If a breaking change does happen, it is announced on the flutter-announce mailing list and in the release notes, and migration guides are published.

Important boundaries to keep in mind:

  • Dart has its own policy. The Dart language follows a separate breaking-change policy with announcements on Dart announce.
  • Engine dependencies are not covered. There is no commitment for other dependencies — a new Skia or Harfbuzz roll could affect contributed tests with no migration guide.
  • Breaking-change docs are snapshots. They are accurate as of the release they were published under and are not kept current, so a workaround from an old release may no longer apply.

If you are not in the registry, you are outside the guarantee. Submitting tests to flutter/tests is the single highest-leverage thing a large Flutter codebase can do.

How a Breaking Change Actually Lands

The repo’s tree-hygiene guide documents a five-step process:

  1. Determine whether it is breaking. Run the existing contributed tests against the change unmodified. If they need edits, it is breaking. Changes that only could hurt someone are announced by courtesy (mailing list, #announcements, the c: API break label) but are not counted as breaking.
  2. Evaluate it. Breaking changes require a full design doc (RFC) in flutter/rfc before coding starts, categorized by subsystem (for example 130 for widgets).
  3. Prepare an opt-in. Land the new behavior behind a flag or a new API rather than replacing the old one. Semantics changes need a three-phase rollout (add API + opt-in, remove old API, remove opt-in); four-phase deprecations are explicitly discouraged.
  4. Land it. Each individual PR breaks no tests, so nothing blocks autorollers while clients migrate.
  5. Document it. Update the migration guide from the official template, push it to the breaking-changes index, email flutter-announce, and tag issues with c: API break so they flow into release notes.

If a change breaks the tree after landing, the policy is blunt: revert first, ask questions later.

Deprecation: How Long Does a Deprecated API Stick Around?

Deprecation is deliberately separate from the compatibility policy, which is exclusively about whether submitted tests fail. The annotation format is enforced by a test in the framework itself:

@Deprecated(
  'Use the replacement API instead. '
  'This will enable an improvement in a future version of Flutter. '
  'This feature was deprecated after v3.44.0-0.1.pre.'
)

The trailing line is always This feature was deprecated after v<beta version at time of deprecation>. A flutter fix (data-driven fix) should ship with the deprecation so dart fix can migrate callers automatically.

The duration question deserves a precise answer, because the wording has changed:

  • The historical rule, still quoted widely in Flutter issue discussions, was that deprecated APIs are removed after a migration grace period of one calendar year after release on the stable channel, or after four stable releases, whichever is longer.
  • The policy as published today says the Flutter team does not remove deprecated APIs on a scheduled basis, and if it does remove one, it follows the same procedure as a breaking change. The repo’s tree-hygiene guide goes further: removing deprecated framework APIs is not currently planned, and while deprecations were removed after a set amount of time in the past, that is not currently in practice.

The practical takeaway is the same either way: you get at least a stable-release cycle’s worth of warning, the analyzer will tell you, and dart fix will usually do the work. But do not assume a calendar — read the @Deprecated message and the breaking-changes index, which still carries entries such as “Deprecated API removed after v3.19” showing that removals have happened.

Reading the Release Notes

Every stable release gets exactly three documents on the release-notes index:

  1. Announcement — the blog post (“What’s new in Flutter x.y”).
  2. Release notes and change log — the narrative of what shipped.
  3. Breaking changes and migrations — the index entry for that release, linking each migration guide.

Two more sources fill the gaps. Bug-fix releases are covered by the CHANGELOG.md on the stable branch of flutter/flutter, not by the release-notes page. Beta changes have no notes page — the SDK archive documents comparing version tags on GitHub instead, for example 3.38.0-0.1.pre...3.38.0-0.2.pre.

The breaking-changes index itself is organized with a Not yet released to stable section at the top, followed by one section per stable release, each listing migration guides alphabetically. Reading the top section before an upgrade tells you what is already coming.

A Safe Pin-and-Upgrade Playbook

$ flutter channel stable
$ flutter upgrade
$ flutter --version          # record the exact version + Dart SDK
$ flutter pub get
$ dart fix --dry-run         # preview automated migrations
$ dart fix --apply           # apply them
$ flutter pub outdated       # check dependency headroom
  • Pin the SDK, commit the lockfile. flutter pub get writes concrete versions into pubspec.lock; commit it so every developer and CI runner resolves identical packages. flutter upgrade moves the SDK; flutter pub upgrade only moves packages within your declared constraints.
  • Test on beta before stable ships. Because roughly every third beta becomes stable, a green beta run in CI is a forecast, not a lottery.
  • Subscribe to flutter-announce and Dart announce. That is where breaking changes are declared, ahead of the release notes.
  • Submit your tests to flutter/tests. You move from “affected by Flutter’s changes” to “consulted before the change lands,” with fixes provided on your behalf.
  • Upgrade deliberately. Batch SDK upgrades into scheduled work items keyed to the published cutoff calendar, and keep a known-good pinned version as the fallback so a bad upgrade is a one-line revert.

Common Pitfalls

  1. Assuming the compatibility policy covers you without contributing tests. The guarantee is scoped to the flutter/tests registry; code outside it can still break.
  2. Treating main (or master) as a stable target. It has no installation bundles, no full test pass, and is intended for Flutter contributors only.
  3. Confusing flutter upgrade with flutter pub upgrade. One moves the SDK, the other moves packages within your constraints.
  4. Ignoring the Not yet released to stable section. It is the earliest warning that a change you depend on is coming.
  5. Assuming engine dependency changes come with guides. Skia and Harfbuzz carry none.
  6. Skipping beta validation. With a public cutoff calendar, a beta CI job costs little and de-risks the upgrade.

Wrapping Up

Flutter’s stability story is more explicit than most frameworks publish: three channels with documented intent, a public release calendar with branch cutoffs, a compatibility guarantee backed by a contributed-test registry, a five-step RFC-driven process for breaking changes, and a migration guide for every one of them. Pin your SDK, commit pubspec.lock, run beta in CI, subscribe to the announce lists, and consider contributing tests. Do that, and the next Flutter release is a scheduled task rather than an incident.

Sources