Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

Deferred imports allow a Dart application to load a library on demand at runtime rather than during the initial application startup. This mechanism instructs the compiler to isolate the deferred code into a separate loadable unit (where supported by the compilation target), which is only fetched, parsed, and executed when explicitly requested via an asynchronous call. To define a deferred import, use the deferred as keywords followed by a mandatory namespace identifier.
import 'package:my_package/heavy_library.dart' deferred as heavy;
Before accessing any members (functions, classes, or variables) of the deferred library, you must explicitly invoke the loadLibrary() method on the defined namespace. This method returns a Future<void> that completes once the library is fully loaded into the isolate’s memory.
Future<void> executeDeferredCode() async {
  // Asynchronously load the library
  await heavy.loadLibrary();
  
  // Access members using the namespace prefix
  var instance = heavy.HeavyClass();
  instance.performAction();
}

Technical Mechanics and Constraints

When implementing deferred imports, the Dart compiler and runtime enforce strict rules regarding how the importing file interacts with the deferred library:
  • Type Annotation Restrictions: You cannot use types from a deferred library as static type annotations (such as in variable declarations, return types, or method signatures) in the importing file. Doing so results in a strict compile-time error. While the underlying reason for this rule relates to runtime memory mechanics—requiring all explicitly declared types to be available in the isolate’s memory when the importing library loads—the restriction is enforced by the analyzer and compiler at compile time. To reference an instance of a deferred type, the idiomatic approach is to rely on type inference using var or final. The analyzer will infer the deferred type and provide static type checking without requiring an explicit annotation. Alternatively, you can type the variable as dynamic or use a shared base class/interface that is imported synchronously.
  • Compile-Time Constants: Constants defined in a deferred library are not treated as compile-time constants in the importing file. They cannot be used in const contexts (e.g., as default parameter values or inside const collections) because their values are not initialized until the library is loaded at runtime.
  • Idempotency: The loadLibrary() function is idempotent. You can invoke await heavy.loadLibrary() multiple times across your application; the Dart runtime guarantees that the library is only fetched and initialized once. Subsequent calls will return a completed Future immediately.
  • Compilation Output:
    • Dart Web (dart2js): The compiler physically splits the deferred library and its dependencies into separate JavaScript files (fragments). These fragments are downloaded over the network only when loadLibrary() is invoked.
    • Dart Native (AOT/JIT): Standard Dart Native (the Dart VM and dart compile exe) does not support splitting deferred code into separate files. It compiles all deferred code into a single executable and loads it upfront. The loadLibrary() call is still required for API compatibility and returns a completed Future, but the code is not physically deferred into separate dynamic libraries. (Note: Splitting AOT code into separate downloadable libraries is a Flutter-specific feature for certain deployment targets, not a standard Dart Native capability).
Master Dart with Deep Grasping Methodology!Learn More