Flutter Upgrade Journey: Null‑Safety Migration, Engine Crash Fixes, and Build Process
The team’s second‑phase Flutter upgrade details why they moved to Flutter 2.x for null‑safety and performance gains, outlines the migration steps and common fixes, describes how they resolved a FlutterEngine destroyContext crash by cherry‑picking a fix, and provides a complete guide to building the iOS engine and gen_snapshot binaries, while previewing future expansion into Flutter Web and cross‑platform front‑ends.
In the previous article we introduced the first round of Flutter promotion. This article focuses on the second round of Flutter upgrades, covering the reasons for upgrading, the migration to null‑safety, custom FlutterEngine handling, and the detailed steps for building the Flutter engine.
Background
Since 2019, the team has been using Flutter. With the release of Flutter 2.x, null‑safety became a critical feature. In Flutter 1.x, a null object would cause runtime exceptions and page freezes. Null‑safety forces developers to declare nullable variables and catches potential null dereferences at compile time, reducing bugs. Performance improvements also motivated the upgrade.
Upgrade Reasons
Flutter 2.x introduces null‑safety, reduces app startup latency, and improves Dart VM garbage‑collection. On low‑end Android devices the initial frame delay can be reduced by up to 300 ms. Over 1,000 null‑safe packages are already available on pub.dev.
Upgrade Implementation
The upgrade involves two main parts: updating the SDK and managing version control for the project.
Switch local environment to SDK 2.8.1.
Run the project to identify compilation errors.
Modify the APIs of the failing libraries.
After local success, push the changes to the CI/CD system for testing and gray release.
Null‑Safety Migration
Use the official tool to check package null‑safety status:
dart pub outdated —mode=null-safetyUpgrade non‑null‑safe dependencies, then fetch the updated packages:
flutter pub get flutter pub upgradeAfter migration, the tool will annotate the code with ! (non‑nullable), ? (nullable), late , late final , and required markers.
Common Errors and Fixes
Error: "The non‑nullable variable 'preferences' must be initialized." – Initialize the variable or make it nullable.
Error: "The method '***' can't be unconditionally invoked because the receiver can be 'null'." – Add null checks or use the null‑aware operator.
Error: Complex generic type modifications (e.g., Map ) – Adjust type definitions accordingly.
Error: "The default 'List' constructor isn't available when null safety is enabled." – Use a null‑safe constructor.
FlutterEngine Crash Issue
After deployment, a crash was observed in native code when the app was closed, related to FlutterEngine.destroyContext . The issue was fixed in later Flutter versions (2.10.2+). Solutions considered:
Upgrade the engine to a version where the bug is fixed.
Cherry‑pick the fix into the 2.8.1 engine and rebuild.
Upgrade the engine to 2.10.4.
Hook destroyContext with safety checks (risky).
Adjust business logic to avoid triggering the crash.
The team chose the conservative approach of cherry‑picking the fix and rebuilding the engine.
Environment Preparation
Install git , Python , Xcode , and depot_tools in /Users/xx/Desktop/flutter-engine :
cd /Users/xx/Desktop/flutter-engine
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.gitConfigure the PATH:
export PATH=/Users/xx/Desktop/flutter-engine/depot_tools:$PATHSync dependencies:
cd /Users/xxx/Desktop/flutter-engine/engine
gclient syncBuild Artifacts
Generate the iOS build:
cd /Users/xxx/Desktop/flutter-engine/engine/src
flutter/tools/gn --ios —unoptimizedRun Ninja to produce the framework:
ninja -C out/ios_debug_unopt && ninja -C out/host_debug_unoptCreate the Flutter.xcframework for debug, profile, and release using the provided Python scripts:
# Debug version
python create_ios_framework.py --dst ~/Desktop/flutter-engine/engine/src/out/vd/ios \
--arm64-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug \
--armv7-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug_arm \
--simulator-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug_simSimilar commands are used for profile and release builds, with the --dsym flag for release.
Create gen_snapshots
Generate gen_snapshot binaries for each build configuration:
# Debug
python create_macos_gen_snapshots.py --dst ~/Desktop/flutter-engine/engine/src/out/vd/ios \
--arm64-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug \
--armv7-out-dir ~/Desktop/flutter-engine/engine/src/out/ios_debug_armSimilar commands apply for profile and release. Simulators use JIT and do not require gen_snapshot .
Future Outlook
After the successful upgrade, the team plans to explore Flutter Web and further integrate Flutter into the broader front‑end ecosystem, including mini‑programs, Taro, H5, and native apps.
HelloTech
Official Hello technology account, sharing tech insights and developments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.