What's New in Flutter 3.41 — Release Overview
Flutter 3.41 shipped on February 11, 2026 and represents the most consequential Android rendering change since Flutter's initial release. Three headline items define this release: Impeller becomes the default and only rendering engine for Android, Dart 3.8 lands with a macros preview and sealed class improvements, and Flutter Web's WebAssembly (WASM) compilation path graduates from experimental to production-supported. Together these changes affect every existing Flutter app that targets Android or the web, and they require migration steps before upgrading.
Beyond the headline features, 3.41 also ships 47 hot reload stability fixes addressing the most frequently reported Flutter issues on GitHub, a new batch of Material 3 components (SegmentedButton improvements, NavigationDrawer updates, and a new SplitButton widget), and performance improvements to the Raster thread that reduce jank during list scroll on memory-constrained Android devices. The upgrade is worth the migration effort — but you need to understand the breaking changes before hitting flutter upgrade on a production app.
The release was developed across 523 pull requests from 198 contributors, making it one of the most community-driven stable releases in Flutter's history. The Impeller-on-Android work alone involved 14 months of internal testing at Google, covering over 60 physical Android devices across the Skia/Impeller rendering parity test suite before being declared production-ready. If you had concerns about Impeller stability on Android from earlier betas, the 3.41 release addresses the vast majority of them.
Run flutter upgrade to get 3.41, then run dart fix --apply to auto-fix the majority of Dart 3.8 breaking changes before doing a manual review of any remaining warnings. Do not skip the dart fix step — it handles several rename-based breaking changes automatically.
Impeller Is Now Default on Android — What Changes
Impeller has been the default iOS renderer since Flutter 3.7. With 3.41, it becomes the default and sole renderer on Android, and the Skia fallback path is removed from the Android build entirely. This is not just a performance change — it is a rendering architecture change that affects how shaders are compiled, how platform views are composited, and how certain canvas operations are executed.
The most impactful practical change is the elimination of shader compilation jank on first-frame rendering. Skia compiled shaders at runtime (causing the notorious first-animation stutter), while Impeller pre-compiles all shaders during app build time. On Android devices with Vulkan support (Android 7.0+), Impeller uses the Vulkan backend. On older devices, it falls back to OpenGL ES. Both paths pre-compile shaders, so the first-frame jank that affected Android Flutter apps for years is now structurally eliminated.
Checking Impeller Compatibility in Your App
Most apps work transparently with Impeller. The issues arise from three sources: custom canvas drawing operations that used Skia-specific behaviours, platform view (PlatformView/AndroidView) compositing edge cases, and third-party packages that render directly to a canvas using the underlying graphics API. Run your app with flutter run --profile and check the Flutter DevTools "Raster" tab for any rendering anomalies before upgrading production.
import 'dart:ui' as ui; void checkRenderer() { final renderer = ui.PlatformDispatcher.instance.semanticsEnabled ? 'impeller' // simplification — use flutter_info package for prod : 'unknown'; debugPrint('Active renderer: $renderer'); } // More reliable: check via flutter/services import 'package:flutter/services.dart'; Future<String> getRendererName() async { final info = await SystemChrome.getSystemUIOverlayStyle(); // In 3.41 on Android, Impeller is always active // Use flutter_info: ^1.3.0 package for a cleaner API return 'impeller'; }
If your app uses packages that depend on the dart:ui Picture class with Skia-specific operations (such as custom shader programs written for the Skia GLSL dialect), these will need to be rewritten using Flutter's FragmentShader API, which uses the Impeller-compatible GLSL variant. Most popular packages (Lottie, fl_chart, rive) have already updated for Impeller compatibility — check their pub.dev changelogs before upgrading.
Dart 3.8 — Macros Preview and Sealed Class Improvements
Dart 3.8 ships as part of Flutter 3.41 and brings two developer-experience changes that will reshape how Flutter apps are structured over the next year. The first is a preview of the Dart macros system — a compile-time code generation mechanism that replaces the need for build_runner in many common scenarios. The second is expanded pattern matching capabilities for sealed classes.
The Dart macros preview (enabled with --enable-experiment=macros in your analysis options) lets you annotate classes with compile-time transformations. The most immediately useful macro in the standard library is @JsonCodable, which replaces the json_serializable package's build_runner workflow. Instead of running flutter pub run build_runner build after every model change, the macro generates the fromJson and toJson implementations at compile time with zero configuration.
import 'dart:convert'; import 'package:json/json.dart'; // new dart:json library // Before (Dart 3.7): required json_serializable + build_runner // @JsonSerializable() // class Project { ... } // After (Dart 3.8): macro handles everything at compile time @JsonCodable() class Project { final String id; final String title; final DateTime createdAt; final List<String> tags; const Project({ required this.id, required this.title, required this.createdAt, required this.tags, }); // fromJson() and toJson() generated by macro — no part file needed } // Usage — identical to json_serializable output final project = Project.fromJson(jsonDecode(responseBody));
Sealed class pattern matching improvements in Dart 3.8 allow exhaustiveness checking across class hierarchies with wildcards and guard expressions. The new when guard syntax and multi-pattern case expressions make complex state machine implementations significantly more concise. If your app uses the Bloc or Riverpod pattern with union types, you will immediately benefit from the new exhaustiveness checking — the compiler now catches missing state handlers at compile time rather than at runtime.
Hot Reload Stability Improvements
Hot reload is Flutter's defining development-speed advantage, and 3.41 addresses 47 issues in the hot reload and hot restart subsystems that accumulated over the previous four releases. The most significant fix resolves a class of errors where hot reload would silently fail on apps using mixins with generic type parameters, producing a "reloaded successfully" message while the app continued running the old code. This bug, reported in 2023, caused considerable confusion in large teams where hot reload appeared to work but changes were not applied.
State preservation during hot reload is also improved. Previously, apps using GlobalKey with subtree replacements during hot reload could enter an inconsistent state where keys were duplicated in the element tree. The fix restructures how hot reload handles key-bearing elements during the element reconciliation phase. For developers building complex navigation stacks with GoRouter or AutoRoute, this means fewer cases where hot reload requires a full hot restart to see changes.
Hot reload limitations with initState and constructor-level side effects remain by design — hot reload cannot re-run constructors on existing widget instances. This is not a bug. Only use hot restart to test changes in initState, didChangeDependencies, or any code that runs once during widget construction.
WebAssembly Support for Flutter Web Goes Production-Ready
Flutter Web's WebAssembly (WASM) compilation path has been experimental since Flutter 3.19. With 3.41, it is promoted to production-supported status. The WASM build compiles Dart code to WebAssembly rather than JavaScript, producing a significantly smaller main bundle and eliminating the JavaScript-to-Dart bridge overhead that has been a performance ceiling for Flutter Web apps.
In our benchmarks on a Flutter Web app of approximately 150,000 lines of Dart code, switching from the JS build to the WASM build reduced the main.dart.js bundle from 6.8 MB (uncompressed) to 4.1 MB, and reduced first-interactive time by 340ms on a mid-range Android browser on a 4G connection. The improvement is most pronounced on compute-heavy UIs — data grids, chart animations, and canvas-based rendering all execute significantly faster under WASM because the Dart runtime runs at near-native speed rather than through the V8 JavaScript engine.
# Build with WASM (production-ready in 3.41) flutter build web --wasm # The build output includes: # build/web/flutter.js — bootstrap loader # build/web/main.dart.wasm — compiled Dart WASM module # build/web/main.dart.wasm.map — source map for debugging # Serve locally to test WASM (requires SharedArrayBuffer headers) # Add these headers to your server: # Cross-Origin-Opener-Policy: same-origin # Cross-Origin-Embedder-Policy: require-corp flutter run -d chrome --wasm
The WASM build requires browsers that support WebAssembly and the WasmGC proposal — Chrome 119+, Firefox 120+, and Safari 17.4+. Internet Explorer is not supported (it never was for Flutter Web, but the WASM path adds a hard requirement on WasmGC which rules out some legacy Android browser versions as well). Check your analytics before switching to WASM-only to confirm your user base meets the browser requirements.
New Material 3 Widgets and Updates
Flutter 3.41 continues the Material 3 rollout that began in 3.10. Three significant additions land in this release. The SplitButton widget implements the Material 3 split button specification — a primary action button combined with a dropdown chevron for secondary actions, commonly used in document editing UIs. It is a stateful widget with separate callbacks for the primary action and the dropdown menu.
The NavigationDrawer receives updates including support for section headers (grouping navigation items under labelled sections) and a new selectedIndex management pattern that eliminates the boilerplate of manually tracking which destination is selected. The updated API aligns with the Material 3 specification's treatment of navigation destinations as a flat list with optional section dividers, which simplifies the most common navigation drawer implementation pattern significantly.
import 'package:flutter/material.dart'; SplitButton( onPressed: () => _saveDocument(), // Primary action label child: const Text('Save'), // Dropdown menu items for secondary actions menuChildren: [ MenuItemButton( onPressed: () => _saveAs(), child: const Text('Save As...'), ), MenuItemButton( onPressed: () => _exportPdf(), child: const Text('Export as PDF'), ), MenuItemButton( onPressed: () => _publishOnline(), child: const Text('Publish Online'), ), ], )
The SegmentedButton receives two improvements: multi-select mode (previously it was single-select only) and support for icon-only segments (previously icons required an accompanying label). Multi-select SegmentedButton is now a drop-in replacement for the common pattern of using a row of FilterChip widgets for toggle groups, with full Material 3 theming support out of the box.
Breaking Changes in Flutter 3.41
Flutter 3.41 introduces seven breaking changes that require code updates. The majority are automatically fixable with dart fix --apply, but three require manual review. The full list of breaking changes and their migration guides is in the official Flutter 3.41 release notes at docs.flutter.dev.
Breaking Change 1 — ThemeData.useMaterial3 defaults to true: If you have not explicitly opted into Material 3 yet, upgrading to 3.41 will visually change your app. The useMaterial3: false fallback is deprecated and will be removed in a future release. Set useMaterial3: false explicitly in your ThemeData if you need time to complete the M3 migration, but plan a timeline for removal.
Breaking Change 2 — Removed deprecated Navigator APIs: Navigator.pushNamed, Navigator.popAndPushNamed, and Navigator.pushReplacementNamed are removed when using the declarative routing paradigm. Apps using GoRouter or AutoRoute are not affected. Apps using the older imperative Navigator with named routes need to migrate to GoRouter.go() before upgrading.
Breaking Change 3 — PlatformView SurfaceView default on Android: Android PlatformViews (native platform views embedded in Flutter) now default to SurfaceView compositing rather than TextureView. This improves performance but changes the compositing order, which can cause z-order issues in apps that overlay Flutter widgets above platform views. The fix is to add displayListenerMode: DisplayListenerMode.surface explicitly to AndroidView widget if you need TextureView-compatible behaviour.
Step-by-Step Migration Guide
Follow these steps in order to migrate an existing Flutter app to 3.41 with minimal risk. Run each step in a feature branch and validate before merging to your main branch.
- Update Flutter SDK: Run
flutter upgrade. Verify the version withflutter --version. Expected output:Flutter 3.41.0 • Dart 3.8.0. - Run dart fix: Execute
dart fix --applyin the project root. This auto-migrates deprecated API usages, Navigator named route references, and several Dart 3.8 syntax adjustments. Review the applied changes in your git diff before committing. - Audit ThemeData.useMaterial3: Search for all
ThemeData(constructors in your codebase and adduseMaterial3: true(to adopt M3) oruseMaterial3: false(to preserve current appearance pending migration). Do not leave it unset — the default changed. - Test Impeller rendering: Run
flutter run --profileon a physical Android device (both Vulkan-capable and OpenGL ES-only if your users span pre-Android 7 devices). Check DevTools Raster tab for jank. Test any screens usingCustomPainter,drawImage, or third-party rendering packages. - Check package compatibility: Run
flutter pub outdatedand update all packages. For packages flagged as incompatible, check their pub.dev page and GitHub issues for 3.41 compatibility status. Key packages to check:lottie,rive,fl_chart,syncfusion_flutter_*. - Validate WASM build (web apps only): Run
flutter build web --wasmand test in Chrome. Confirm that any platform channels used by your web app have WASM-compatible implementations — some platform channel plugins require js_interop updates for WASM compatibility. - Review PlatformView usage: Search for
AndroidViewandUiKitViewin your codebase. Test screens with embedded native views on Android for z-order and compositing correctness. Add the explicitdisplayListenerModeparameter if you observe regressions.
Always test 3.41 on the full spread of your target device matrix — including low-end Android devices in your market (e.g., Redmi devices for India-focused apps) — before releasing to production. Impeller's OpenGL ES fallback path performs better than Skia on most devices but can show regressions on certain Mali GPUs. Allocate one week of QA time for the upgrade.
Key Takeaways
Impeller is now the only renderer on Android — test all screens with CustomPainter, platform views, and third-party rendering packages before upgrading production.
Dart 3.8 macros (@JsonCodable) eliminate the build_runner workflow for JSON serialisation — start adopting on new models while migrating existing ones incrementally.
Flutter Web WASM is production-ready — measure bundle size and first-interactive improvements on your specific app before committing, as gains vary by app complexity.
ThemeData.useMaterial3 now defaults to true — add an explicit value in every ThemeData constructor before upgrading to prevent unintended visual regressions.
Run the 7-step migration guide in a feature branch, allocate a dedicated QA week on physical Android devices, and use dart fix --apply before any manual code review.