skip to content

In Dart, what does `import ... deferred as` do, and what restrictions apply to a deferred library before and after `loadLibrary()` completes?

level: middleimportance: nice to knowfreq 15%

answer

  1. lazy loading on the web
  2. prefix is mandatory
  3. loadLibrary returns a Future
  4. no types, no constants
  5. loads once however often called

basics

~20 s

deferred as lets a web app download a library on demand: you await prefix.loadLibrary() before touching its members. Its types cannot appear in the importing file, its constants are not constants there, and the dart tool supports deferral only for web targets.

solid answer

~40 s

`import 'package:charts/charts.dart' deferred as charts;` marks the library for **lazy loading**, so a web build can split it out and fetch it when needed, cutting startup time. A deferred import **must** use a prefix, and Dart implicitly adds `loadLibrary()` to that prefix; it returns a `Future`, and you `await charts.loadLibrary()` before using any member. Calling it again is harmless: the library loads once. Two restrictions apply in the importing file: you cannot use the deferred library's **types** (in annotations, `is` checks or generics), and its **constants are not constants** there. Shared interfaces go in a library imported normally by both sides. The `dart` tool supports deferral only for web targets; Flutter has its own deferred components.

code

dart · 12 lines
dart
import 'src/report_view.dart'; // interface, imported normally
import 'src/reports_impl.dart' deferred as reports;

ReportView? _cached;

Future<ReportView> openReports() async {
  await reports.loadLibrary(); // cheap after the first call
  return _cached ??= reports.buildReportView();
}

// Not allowed here: reports.ReportImpl as a type,
// or reports.defaultPageSize inside a const expression.

go deeper

for a junior

Recognise the syntax: deferred as a prefix, then await prefix.loadLibrary() before using anything from it.

for a middle

Explain the type and constant restrictions and the shared-interface workaround, and that loadLibrary loads only once.

for a senior

Judge when a web split is worth the extra round trip, and guard every path to deferred code so nothing runs before loading.

for a principal

Decide which features of a large web app justify deferral against the added loading states and complexity at call sites.

## What deferred loading is for **Deferred loading**, also called lazy loading, lets a Dart web app download part of its code **only when it is needed** instead of at startup. dart.dev lists three motivations: - reduce a web app's **initial startup time**; - **A/B testing** alternative implementations of an algorithm; - load **rarely used functionality**, such as optional screens and dialogs. The compiler splits the deferred library and whatever only it needs into a separate output file that is fetched on demand. ## The syntax ```dart import 'package:greetings/hello.dart' deferred as hello; Future<void> greet() async { await hello.loadLibrary(); hello.printGreeting(); } ``` 1. The import uses `deferred as <prefix>`; a deferred import **must** have a prefix. 2. Dart **implicitly inserts** a `loadLibrary()` function into that prefix's namespace. It returns a `Future`. 3. After the future completes, members are used through the prefix as normal. 4. `loadLibrary()` may be called **multiple times**; the library is **loaded only once**. Using a member before the load has completed is a mistake: where deferral is real, the code simply is not there yet. Put the `await` on every path that can reach the deferred code, or gate the UI behind the future. ## Restrictions in the importing file The docs list two restrictions that follow from "the code might not be there yet": | Restriction | Why | Workaround | |---|---|---| | No **types** from the deferred library in the importing file | a type annotation, `is` test or type argument would need the class before it is loaded | declare an interface in a library imported normally by both files | | The deferred library's **constants are not constants** here | a `const` context needs the value at compile time | use the value at run time after loading, or move the constant to a shared library | The interface pattern looks like this: `lib/src/report_view.dart` declares `abstract interface class ReportView`, the heavy `lib/src/reports_impl.dart` implements it, and the caller imports `report_view.dart` normally and `reports_impl.dart` deferred. After loading, the caller holds the object through the `ReportView` type. ## Where deferral actually happens The dart.dev page is explicit that the **`dart` tool doesn't support deferred loading for targets other than web**. Native `dart compile` executables do not split code this way. The dartdevc changelog notes that since Dart 3.11 the `Future` from `loadLibrary()` always completes asynchronously, matching dart2js, even when the runtime could not defer. Flutter apps are different again: Flutter implements deferred loading on Android as **deferred components**, with its own build configuration and Play Store delivery. That mechanism is a Flutter app-size topic; the Dart-level syntax above is the shared entry point. ## History Before the `deferred as` syntax, `dart:async` had a `DeferredLibrary` class; it was deprecated and has been **removed**. Current code uses only the import syntax. ## When to reach for it - A large, rarely visited part of a Dart or Flutter web app, such as an admin console or a charting page. - A feature behind a flag you only want some users to download. - Not for small libraries: each split adds a network round trip and complexity at every call site.

  • In Dart, why can't you write `reports.ReportImpl? cached;` when `reports` is a deferred prefix?
    Types from a deferred library are not usable in the importing file, because the class might not be loaded when the annotation must be checked. Declare an interface in a library imported normally by both files and type the variable with that interface instead.
  • Does `deferred as` shrink a Dart command-line executable built with `dart compile exe`?
    No. The dart tool supports deferred loading only for web targets, so a native executable is not split into separately loaded pieces. Deferral pays off in web builds, and Flutter apps use deferred components instead.

saying these in an interview costs you the question

  • A deferred import can omit the prefix
  • Calling loadLibrary twice downloads the library twice
  • Deferred library types can be used in annotations before loading
  • The dart tool splits native executables with deferred imports
  • Deferred constants can still be used in const expressions