Published November 9, 2026
Flutter 3.50 Breaking Change Set Is Taking Shape: Intent Verification, Engine Flag Restrictions, and Six Migration Guides
The 3.50 section of Flutter's breaking change documentation now has content, and the list is unusual for a release that has not reached stable. Seven migration entries are published under a release the docs still mark as tentative, while the beta branch itself moved to 3.50.0-0.4.pre on November 3. Two of those entries are security work on the Android embedder, and together they change the trust model for how a Flutter app is launched.
Flutter's stable release notes confirm the shape of the cycle: 3.50 is the next feature release, and the migration list is the part of it that can break working code on upgrade day. Reading it now is cheaper than reading it in a bug report later.
Intent Verification for App Entrypoints
The first entry is titled stricter Android Intent verification for app entrypoints. The Flutter Android embedder, meaning FlutterActivity and FlutterFragmentActivity, now verifies the sender of an Intent before it processes five specific extras: EXTRA_INITIAL_ROUTE, EXTRA_DART_ENTRYPOINT, EXTRA_DART_ENTRYPOINT_ARGS, EXTRA_CACHED_ENGINE_ID, and EXTRA_CACHED_ENGINE_GROUP_ID.
The previous behaviour was permissive by design. Anything that could start your activity could also tell it which route to open, which Dart entrypoint to run, and even which cached engine instance to reuse. That last part is the serious one. A cached engine can carry an authenticated session in memory, so reusing another app's engine group is not a navigation trick, it is a session bypass.
Under the new rule, an Intent counts as verified in one of three ways. If the activity is declared with android:exported set to false, it is trusted automatically. On Android 14 and later, the launching UID must match the application UID as reported by getLaunchedFromUid. On Android 13 and earlier, the calling package must match the app package, which requires the caller to use startActivityForResult or startActivityIfNeeded rather than a plain startActivity.
Deep links keep working, but they must resolve against an intent-filter declared in the manifest unless the Intent is verified as self sent. When verification fails in a release build, the embedder ignores the extras and falls back: initial route to command line arguments or the io.flutter.InitialRoute manifest metadata, then to a literal slash; entrypoint to the io.flutter.Entrypoint metadata, then to main; entrypoint arguments and cached engine identifiers to null, with no fallback path at all.
Those null returns are where teams will get hurt. An app that relied on EXTRA_CACHED_ENGINE_ID to attach to a pre-warmed engine will now start a cold engine instead, a latency regression rather than a crash, and therefore easy to miss in testing.
- Audit every Intent your app exports and document which extras it accepts
- Move route and entrypoint configuration into manifest metadata if it must be trusted in production
- Test cached engine flows on a release build, since a cold start will not raise an error
- Confirm deep link intent filters cover every URI your app advertises
Engine Flags Are Ignored in Release Mode
The second security entry follows from the same reasoning. Engine configuration flags such as --dart-flags, --route, and tracing options could previously be passed at launch through Intent extras in every build mode. That gave tools a way to configure a running binary without touching source or manifest, and it gave anyone else on the device a parameter injection surface, including spoofing something like --aot-shared-library-name.
Release builds now ignore those flags entirely and log a warning, reading configuration strictly from the compiled manifest or from programmatic setup performed before engine initialisation. Debug and profile builds keep the old behaviour, since local benchmarking and test harnesses depend on it.
The consequence that will surprise people is in the CLI. When you run flutter run --release --use-application-binary, the tool cannot rewrite a manifest already compiled into the binary, so it now exits with a fatal error if engine flags are supplied. A silent no-op in a release pipeline would be worse than a failed command. Standard Gradle builds are unaffected because the CLI injects flags into the manifest during compilation.
One internal API moved as part of this work: FlutterActivityAndFragmentDelegate.Host.getFlutterShellArgs is deprecated in favour of getFlutterEngineFlags, so embedding code overriding the old method needs a rename.
The Rest of the 3.50 List
Five more entries round out the release. The largest in reach is migration to the standalone material_ui and cupertino_ui packages, continuing the widget library decoupling. A related note covers DropdownButtonFormField gaining an enabled parameter while onChanged becomes optional, shipped in material_ui 1.8.0.
Three rendering and platform entries are smaller but deserve a line each. Soft hyphens now render at line breaks, which changes layout for any text containing that character, since the hyphen becomes visible at the break point instead of being ignored. On web, viewport-fit=cover changes how SafeArea behaves on notched and rounded displays. And WidgetSpan children in right-to-left text now follow corrected bidi ordering, which will shift inline widget placement in Arabic, Hebrew, and Urdu layouts.
- Run dart fix after upgrading, since several entries in this list have automated migrations
- Re-record golden tests that contain soft hyphens or inline widgets inside RTL text
None of this is shipping tomorrow. The 3.50 branch cut in mid October, and the pre-release tags since then have been engine version syncs rather than feature work: 0.1.pre on October 14 through 0.4.pre on November 3. That cadence is normal for a beta branch kept in step with engine rolls, and it says nothing about a stable date.
What it does say is that the migration guides are being written while the release is still negotiable. The two security entries are the ones to act on early, because both concern behaviour that only differs in release builds. An app can pass every debug test and still lose its deep links, its pre-warmed engine, or its launch flags in production.