Load sequence, performance, and memory
This page describes the breakdown of the steps involved to show a Flutter UI. Knowing this, you can make better, more informed decisions about when to pre-warm the Flutter engine, which operations are possible at which stage, and the latency and memory costs of those operations.
Loading Flutter
#Android and iOS apps (the two supported platforms for integrating into existing apps), full Flutter apps, and add-to-app patterns have a similar sequence of conceptual loading steps when displaying the Flutter UI.
Finding the Flutter resources
#Flutter's engine runtime and your application's compiled Dart code are both bundled as shared libraries on Android and iOS. The first step of loading Flutter is to find those resources in your .apk/.ipa/.app (along with other Flutter assets such as images, fonts, and JIT code, if applicable).
                  This happens when you construct a FlutterEngine for the
                  first time on both Android
                  
                  and iOS
                   APIs.
                
Loading the Flutter library
#After it's found, the engine's shared libraries are memory loaded once per process.
                  On Android, this also happens when the
                  FlutterEngine
                   is constructed because the
                  JNI connectors need to reference the Flutter C++ library.
                  On iOS, this happens when the
                  FlutterEngine
                   is first run,
                  such as by running runWithEntrypoint:.
                
Starting the Dart VM
#The Dart runtime is responsible for managing Dart memory and concurrency for your Dart code. In JIT mode, it's additionally responsible for compiling the Dart source code into machine code during runtime.
A single Dart runtime exists per application session on Android and iOS.
                  A one-time Dart VM start is done when constructing the
                  FlutterEngine
                   for the first time on
                  Android and when running a Dart entrypoint
                  
                  for the first time on iOS.
                
At this point, your Dart code's snapshot is also loaded into memory from your application's files.
This is a generic process that also occurs if you used the Dart SDK directly, without the Flutter engine.
The Dart VM never shuts down after it's started.
Creating and running a Dart Isolate
#After the Dart runtime is initialized, the Flutter engine's usage of the Dart runtime is the next step.
                  This is done by starting a Dart Isolate
                   in the Dart runtime.
                  The isolate is Dart's container for memory and threads.
                  A number of auxiliary threads
                   on the host platform are
                  also created at this point to support the isolate, such
                  as a thread for offloading GPU handling and another for image decoding.
                
                  One isolate exists per FlutterEngine instance, and multiple isolates
                  can be hosted by the same Dart VM.
                
                  On Android, this happens when you call
                  DartExecutor.executeDartEntrypoint()
                  
                  on a FlutterEngine instance.
                
                  On iOS, this happens when you call runWithEntrypoint:
                  
                  on a FlutterEngine.
                
                  At this point, your Dart code's selected entrypoint
                  (the main() function of your Dart library's main.dart file,
                  by default) is executed. If you called the
                  Flutter function runApp()
                   in your main() function,
                  then your Flutter app or your library's widget tree is also created
                  and built. If you need to prevent certain functionalities from executing
                  in your Flutter code, then the AppLifecycleState.detached
                  enum value indicates that the FlutterEngine isn't attached
                  to any UI components such as a FlutterViewController
                  on iOS or a FlutterActivity on Android.
                
Attaching a UI to the Flutter engine
#A standard, full Flutter app moves to reach this state as soon as the app is launched.
                  In an add-to-app scenario,
                  this happens when you attach a FlutterEngine
                  to a UI component such as by calling startActivity()
                  
                  with an Intent
                   built using FlutterActivity.withCachedEngine()
                  
                  on Android. Or, by presenting a FlutterViewController
                  
                  initialized by using initWithEngine: nibName: bundle:
                  
                  on iOS.
                
                  This is also the case if a Flutter UI component was launched without
                  pre-warming a FlutterEngine such as with
                  FlutterActivity.createDefaultIntent()
                   on Android,
                  or with FlutterViewController initWithProject: nibName: bundle:
                  
                  on iOS. An implicit FlutterEngine is created in these cases.
                
                  Behind the scene, both platform's UI components provide the
                  FlutterEngine with a rendering surface such as a
                  Surface
                   on Android or a CAEAGLLayer
                   or CAMetalLayer
                  on iOS.
                
                  At this point, the Layer
                   tree generated by your Flutter
                  program, per frame, is converted into
                  OpenGL (or Vulkan or Metal) GPU instructions.
                
Unless stated otherwise, the documentation on this site reflects Flutter 3.35.5. Page last updated on 2025-10-1. View source or report an issue.