Flutter 3.47 Is Here: Material and Cupertino Just Left the SDK

Bilal Fali··8 min read
Flutter 3.47 Is Here: Material and Cupertino Just Left the SDK
Advertisement

Flutter 3.47 landed on August 12, and this is the first release in a while where I actually stopped what I was doing and read the whole changelog before running flutter upgrade on Bacify. Most releases are a mix of small fixes and one or two headline features you skim past. This one restructures something that's been baked into every Flutter app since the framework existed: Material and Cupertino no longer live inside the core SDK.

If that sounds like an internal plumbing detail that doesn't affect you, it will the moment you hit the Fall stable deprecation in November. Let's go through what actually matters here, in the order I'd prioritize fixing it.

The Big One: material_ui and cupertino_ui Are Now Separate Packages

Since Flutter existed, package:flutter/material.dart and package:flutter/cupertino.dart shipped bundled inside the core SDK. Every quarterly Flutter release, Material and Cupertino updates were stuck waiting for the whole SDK release train. Flutter 3.47 breaks that dependency material_ui and cupertino_ui hit 1.0 on pub.dev as standalone packages, which means they can now ship fixes and new components weekly instead of quarterly.

Your current code still works. The core SDK still includes these libraries in 3.47. But the original libraries are scheduled for formal deprecation in the Fall stable release in November so this isn't optional homework, it's a deadline.

Migrating Is One Command

dart fix --apply --code=migrate_design_widgets

This rewrites your imports from package:flutter/material.dart and package:flutter/cupertino.dart to the new standalone packages automatically. I ran this on a smaller side project first before touching Bacify's codebase, and it handled about 90% of the import rewriting cleanly.

The one snag and the docs actually call this out as a known early bug is the pubspec.yaml update sometimes doesn't happen automatically. If that hits you:

flutter pub add material_ui
flutter pub add cupertino_ui  # if you use Cupertino widgets
dart fix --apply

If You Maintain a Package, Not Just an App

If you publish anything on pub.dev, treat this migration as a major version bump, not a patch. The team is explicit about this in the release notes, and it makes sense anyone depending on your package now needs the standalone packages too. Bump accordingly or you'll get confused bug reports in a few weeks.

The Compatibility Bridge Actually Matters

Here's the part that saves you from a painful all-or-nothing migration: if some of your dependencies haven't migrated yet but you want to move forward anyway, wrap your app in MaterialUiCompatibilityBridge:

import 'package:material_ui/material_ui.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF6750A4)),
      ),
      builder: (BuildContext context, Widget? child) {
        return MaterialUiCompatibilityBridge(child: child!);
      },
      home: const HomeScreen(),
    );
  }
}

This is what lets you migrate your own code today without waiting on every plugin in your pubspec.yaml to catch up.

Localizations Moved Too

flutter_localizations got unbundled along with the design packages. If you're supporting Arabic/RTL like I do for Bacify, your localization delegate setup gets simpler, not more complicated:

// Before
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter/material.dart';

localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
  GlobalCupertinoLocalizations.delegate,
  GlobalMaterialLocalizations.delegate,
  GlobalWidgetsLocalizations.delegate,
],
// After
import 'package:material_ui/material_ui.dart';

localizationsDelegates: GlobalMaterialLocalizations.delegates,

One line instead of three. GlobalMaterialLocalizations.delegates now bundles Cupertino and Widgets delegates automatically.

iOS 27 Is Coming and Flutter Bumped Its Minimums

With Xcode 27, iOS 27, and macOS 27 landing this fall, Flutter 3.47 raises the minimum supported OS versions to prepare:

Platform

Old minimum

New minimum

iOS

13

15

macOS

10.15

12

If your app still supports iOS 13 or 14 users, check your analytics before you upgrade — you may be about to drop support for a real slice of your install base. For most apps in 2026 this is a non-issue since iOS 13/14 usage is negligible, but it's worth an actual check rather than an assumption.

UIScene Is Now Mandatory, Not Optional

iOS 27 requires all UIKit-based apps to adopt the UIScene lifecycle. Apps built with Xcode 27 that skip this will fail to launch entirely not a warning, a hard crash on startup.

The Flutter CLI handles this automatically for most apps during the build. But if you have custom native code in your AppDelegate, or you're using plugins that still hook into the legacy application lifecycle, you need to migrate manually. Check the UIScene/Delegate Adoption Guide before you get surprised by this in a TestFlight build.

Intel Macs Are on Their Way Out

Flutter has disabled automated test runs on Intel hardware, and the CLI now warns when building on Intel hosts. These warnings become errors in a future release. If you're still building macOS releases on an old Intel machine, that clock is ticking you can opt into ARM64-only builds now with:

flutter config --enable-macos-arm64-only

Swift Package Manager: 92 of the Top 100 Plugins Migrated

CocoaPods is in maintenance mode. Unmigrated plugins get lower pub.dev scores and will eventually stop working entirely. If you turned SwiftPM off before because a plugin you depended on wasn't ready, it's worth trying again the ecosystem moved fast on this one:

flutter config --enable-swift-package-manager

Impeller Is Now Default on Desktop

This is the one I'm genuinely happy about. Impeller Flutter's shader-precompiled rendering engine that already eliminated jank on mobile is now the default renderer for macOS, Windows, and Linux in 3.47.

If you've shipped a Flutter desktop app before, you know the first-animation stutter Skia used to cause while it compiled shaders at runtime. Impeller precompiles at build time instead, so that stutter is gone from the first frame. Combined with Wide Gamut Color now active by default on macOS, desktop Flutter apps in 3.47 genuinely look and feel closer to native than they did two releases ago.

If something breaks and you need to fall back temporarily:

  • macOS: set FLTEnableImpeller to false in Info.plist

  • Windows: project.set_impeller_switch(flutter::ImpellerSwitch::Disabled) in main.cpp

  • Linux: fl_dart_project_set_enable_impeller(project, FALSE) in my_application.cc

Don't get too attached to that escape hatch fallback options are getting removed in a future release. If you hit a real bug forcing you to revert, file it now rather than quietly relying on the flag.

Desktop Windowing Keeps Maturing

Linux and Windows now support popup windows for native context menus and utility palettes, and you can query windowHandle directly to get a pointer to the underlying native window (HWND, NSWindow, GtkWindow) for advanced native integration like dockable panes.

Windows and Linux also picked up flavor support this release genuinely useful if you're shipping a desktop app with a free/pro split or white-labeled variants, since you can now vary assets per flavor the same way you already could on mobile.

Wasm Is Getting Closer to Default

Flutter is actively working toward Wasm-by-default for web builds. You can opt in today:

flutter build web --release --wasm

The catch: Wasm requires migrating off dart:html to package:web. If your web app hasn't touched JS interop directly, you're probably fine already. If it has, budget time for this before Wasm becomes the default and you're forced into it under deadline pressure instead of on your own schedule.

Large web apps also get experimental deferred loading support on Wasm, letting you split into lazy-loaded modules for faster initial load:

flutter build web --release --wasm --enable-wasm-deferred-loading

Widget Previews Are Stable

Flutter Widget Preview graduated to stable in 3.47 render and iterate on individual UI components without rebuilding your whole app. It now caches locally in a .widget_preview/ folder for faster startup, and web previews automatically sync your project's web/ assets and custom index.html theming.

If you haven't tried Widget Previews yet, this is the release where it's worth adding to your daily workflow, especially for anyone iterating on design-heavy screens.

Everything Else Worth Knowing

A few smaller fixes that are easy to miss in the changelog but genuinely useful depending on your app:

  • Android: Fixed a virtual keyboard bug where modifier keys like Shift could get stuck mid-event

  • Android build matrix: Verified against Java 17 minimum, AGP 9.1.0, Gradle 9.3.1, targeting API 36

  • iOS code signing: The CLI now shows both Team ID and Team Name when selecting a certificate small, but saves a lookup every time you've forgotten which team is which

  • Text selection: Selection handles on mobile stay stable during minor scrolling, and Android selection handles no longer overlap the context menu near the top of the screen

  • ImageIcon: New useOriginalColors: true flag if you've ever fought Flutter forcibly recoloring an icon you didn't want tinted

What I'm Actually Doing on Bacify This Week

Realistically, here's my priority order for migrating a production app:

  1. Run the widget migration tool : on a branch, not main dart fix --apply --code=migrate_design_widgets

  2. Check the UIScene lifecycle : Bacify doesn't have custom AppDelegate code, so this should be automatic, but I'm testing an Xcode 27 beta build before assuming

  3. Leave Impeller desktop alone : I don't ship desktop builds yet, so this one's free

  4. Hold off on Wasm : not worth the dart:html migration cost until it's closer to default

Your priority order will differ based on what you actually ship. But if you do nothing else from this release, do the design widget migration before November that's the one with an actual deadline attached.

Advertisement
Share

Did this article save you time?

I write these for free. If it helped, a coffee keeps me going — and more articles coming.

Buy me a coffee

Comments

Comments

Leave a comment

0/2000

Comments appear after review.