Glossary
A glossary reference for terminology used across dart.dev.
The following are definitions of terms used across the Dart documentation.
AOT
Ahead-of-Time (AOT) compilation produces optimized native binaries at build time.
Ahead-of-Time (AOT) compilation converts Dart code to native machine code at build time. This results in smaller, faster executables that start instantly and run with consistent performance. AOT builds are tree-shaken to remove unused code and are used for deploying production apps.
Related docs and resources
Application package
A Dart package that contains a runnable application.
An application package is a Dart package that contains a program or app with a main entrypoint. Meant to be run directly, either on the command line, in a browser, or by another embedder, such as provided by Flutter.
Application packages can have dependencies on other packages, but are never depended on themselves. Unlike regular packages, they are not intended to be shared.
Application packages should check their lockfiles into source control, so that everyone working on the application and every location the application is deployed has a consistent set of dependencies.
Related docs and resources
Assist
An automated, local code edit targeted at making common improvements to code.
An assist is an automated, local code edit targeted at
making common improvements to code. Examples of
assists include converting
switch statements to switch
expressions, reversing the then and
else blocks in an
if statement, and inserting widgets into
a widget structure.
Related docs and resources
Asynchronous
Programming paradigm in Dart where operations like I/O or network calls complete later without blocking the event loop.
Dart uses a single-threaded event loop model for asynchronous programming, allowing non-blocking execution of time-consuming tasks such as file I/O, HTTP requests, and timers. Unlike synchronous code that blocks until completion, async operations queue through microtasks/events, keeping UIs responsive.
Key components include:
-
Futures: Represent a value
available later, created by APIs like
http.get(). - async/await: Dart keywords for writing maintainable and readable asynchronous code.
- Streams: Handle sequences of async events like data from web sockets.
- Isolates: Enable true parallelism through independent execution contexts with message passing.
Bottom type
A type that has no values and is a subtype of all other types.
The bottom type in Dart is the type that has no values and is considered a subtypeSubtypeA type that can be used wherever a value of its supertype is expected. Learn more of every other type.
In Dart, the bottom type is represented by the
Never type.
This means a value of type Never can be
used anywhere, because such a value can never actually
exist. It's most often used as the
return type of functions to indicate
they never return, such as for those
that throw exceptions or loop forever.
For example, the following fail function
always throws an exception, so it's declared with a
return type of Never to indicate that it
never returns:
Never fail(String message) {
throw Exception(message);
}
void main() {
String result = fail('Oops'); // OK: Never is a subtype of String.
}
Since fail never returns, assigning it to
a String is allowed.
Related docs and resources
Callback
A function passed as an argument to another function to be called later.
A callback is a function that you pass as an argument to another function so that it can be invoked (or "called back") at a later time.
Callbacks are often used for:
- Event handling.
- Customizing the behavior of APIs.
- Asynchronous operations, such as responding to the result of a future.
For example, in the following snippet, the
printResult function is passed as a
callback to doOperation, and is called
after the operation finishes:
void doOperation(int a, int b, void Function(int) callback) {
final result = a + b;
callback(result);
}
void printResult(int value) {
print('The result is $value.');
}
void main() {
doOperation(3, 4, printResult); // Prints: The result is 7.
}
Related docs and resources
Closurization
The process of turning a method or function into a closure.
Closurization is the process where Dart turns a method or function into a function object (or tear-off) that can be stored, passed, or invoked later.
A tear-off is a reference to an existing function or method that evaluates to a function object for that entity, but does not call it. When you tear off a function, the runtime (or compiler) wraps it into a closure so that it behaves like any other function object in Dart.
Example:
class Greeter {
void greet(String name) {
print('Hello, $name!');
}
}
void main() {
final greeter = Greeter();
// Tear-off of the instance method `greet`.
final sayHello = greeter.greet;
sayHello('Dart'); // Prints: Hello, Dart!
}
In this example, greeter.greet is
closurized into a function object that captures the
instance greeter. When you call
sayHello, it executes as if you had
called greeter.greet(...) directly.
Closurization ensures that tear-offs can be used wherever a function is expected.
Related docs and resources
Code asset
Compiled native code that is bundled with a Dart app
using a build hook and can be used through
dart:ffi.
A code asset is compiled code that
isn't written in Dart but can be packaged and used by
a Dart application. Examples include shared libraries
(.so, .dll,
.dylib) built in languages like C, C++,
or Rust.
Code assets can be bundled with a Dart package using a build hook. Dart code can call into these assets using FFI.
Historically, code assets were also referred to as native assets or native code assets.
Code assets are useful for:
- Integrating with existing native libraries.
- Accessing system capabilities not directly exposed in Dart.
- Sharing performance-critical functionality across platforms.
For example, a Dart application could include a native
.so library that exposes specific
functions, and call those functions from Dart.
Related docs and resources
Combinator
A keyword clause that limits or modifies what's imported or exported.
In Dart, a combinator is a clause
that follows an import or
export directive to limit or modify the
set of names brought into scope.
Dart supports two types of combinators:
-
show— explicitly includes specific names. hide— excludes specific names.
Combinators help control namespace pollution and avoid conflicts when multiple libraries define symbols with the same name.
Example using show:
import 'dart:math' show pi, sqrt;
void main() {
print(pi); // Accessible
print(sqrt(9)); // Accessible
// print(Random()); // Error: Random is not imported.
}
Example using hide:
import 'dart:math' hide pi;
void main() {
// print(pi); // Error: pi is hidden.
print(Random()); // Accessible
}
Related docs and resources
Constant context
A region of code where the const keyword is implied and everything within that region must be a constant.
A constant context is a region of code in
which it isn't necessary to include the
const keyword because it's implied by the
fact that everything in that region is required to be
a constant. The following locations are constant
contexts:
-
Everything inside a list, map or set literal that's prefixed by the
constkeyword. For example:dartvar l = const [/*constant context*/]; -
The arguments inside an invocation of a constant constructor. For example:
dartvar p = const Point(/*constant context*/); -
The initializer for a variable that's prefixed by the
constkeyword. For example:dartconst v = /*constant context*/; Annotations.
-
The expression in a
caseclause. For example:dartvoid f(int e) { switch (e) { case /*constant context*/: break; } }
Related docs and resources
Context type
The type that the surrounding code expects from an expression.
The context type is the type that the surrounding code expects from an expression, such as a variable type, a parameter type, or a return type.
Dart uses the context type to interpret and infer meaning from expressions, including:
-
Type inference ("downwards inference"):
dartList<int> list = [];The context type
List<int>lets the compiler infer the list type as<int>[]. -
Implicit downcast:
dartString asString(dynamic value) => value;The return context is
String, so Dart inserts an implicit downcast. -
Literal interpretation:
dartdouble d = 0;The context type
doublemakes0behave like0.0. -
Static access shorthand (dot shorthand):
dartint x = .parse(input);The context type is
int, so.parseresolves toint.parse(input).
Some expressions have no context type, including:
-
When used as statements:
Expressions likeset.remove(value);are used only for their effect, not their value, so no type is expected. -
When the context type is inferred from the expression:
For example, invar list = [1];, the list literal has no context type. Dart infersList<int>from the contents and assigns that type to the variable.
Related docs and resources
Dart SDK constraint
The versions of Dart that a package supports.
The range of Dart SDK versions that a package itself
declares it supports. An SDK constraint is specified
using normal
version constraint
syntax, but in a special
environment section
in the pubspec.
Related docs and resources
Definite assignment
The determination of whether a variable has definitely been assigned a value before it's used.
Definite assignment analysis is the process of determining, for each local variable at each point in the code, which of the following is true:
- The variable has definitely been assigned a value (definitely assigned).
- The variable has definitely not been assigned a value (definitely unassigned).
- The variable might or might not have been assigned a value, depending on the execution path taken to arrive at that point.
Definite assignment analysis helps find problems in code, such as places where a variable that might not have been assigned a value is being referenced, or places where a variable that can only be assigned a value one time is being assigned after it might already have been assigned a value.
For example, in the following code the variable
s is definitely unassigned when it's
passed as an argument to print:
void f() {
String s;
print(s);
}
But in the following code, the variable
s is definitely assigned:
void f(String name) {
String s = 'Hello $name!';
print(s);
}
Definite assignment analysis can even tell whether a
variable is definitely assigned (or unassigned) when
there are multiple possible execution paths. In the
following code the print function is
called if execution goes through either the true or
the false branch of the if statement, but
because s is assigned no matter which
branch is taken, it's definitely assigned before it's
passed to print:
void f(String name, bool casual) {
String s;
if (casual) {
s = 'Hi $name!';
} else {
s = 'Hello $name!';
}
print(s);
}
In flow analysis, the end of the
if statement is referred to as a
join—a place where two or more execution
paths merge back together. Where there's a join, the
analysis says that a variable is definitely assigned
if it's definitely assigned along all of the paths
that are merging, and definitely unassigned if it's
definitely unassigned along all of the paths.
Sometimes a variable is assigned a value on one path
but not on another, in which case the variable might
or might not have been assigned a value. In the
following example, the true branch of the
if statement might or might not be
executed, so the variable might or might be assigned a
value:
void f(String name, bool casual) {
String s;
if (casual) {
s = 'Hi $name!';
}
print(s);
}
The same is true if there is a false branch that
doesn't assign a value to s.
The analysis of loops is a little more complicated,
but it follows the same basic reasoning. For example,
the condition in a while loop is always
executed, but the body might or might not be. So just
like an if statement, there's a join at
the end of the while statement between
the path in which the condition is
true and the path in which the condition
is false.
Related docs and resources
Dependency
A Dart package that a package relies on.
A dependency is any other Dart package that a package relies on. If your package wants to import code from some other package, that package must be a dependency of yours first. Dependencies are specified in your package's pubspec file with the syntax described in Package dependencies.
To view the dependencies used by a package, use
pub deps.
Related docs and resources
Dependency graph
A representation of how components in a system depend on each other.
A dependency graph is a directed graph that shows the relationships between different components in a system, such as files, modules, packages, functions, or assets. Each node represents a component, and each edge represents a dependency between two components.
In Dart and Flutter, dependency graphs are commonly used by tools like:
- The compiler, to determine build order.
- The package manager, to resolve package versions.
- Build systems, to detect what needs to be rebuilt.
- Tree shaking, to remove unused code.
For example, if file main.dart imports
utils.dart, and
utils.dart in turn imports
math.dart, the dependency graph helps the
compiler understand the correct order to process these
files and which parts of the program are actually
used.
Dependency graphs are essential for:
- Efficient builds
- Dead code elimination (tree shaking)
- Dependency resolution
- Incremental compilation
Related docs and resources
Dependency source
A kind of place that pub can get packages from.
A type of repository or location that pub can retrieve packages from. A source isn't a specific place like the pub.dev site or a specific git URL. Each source describes a general procedure for accessing a package.
As an example, git is one of the supported dependency source. The git source knows how to download packages given a git URL. Several different supported sources are available.
Related docs and resources
Dot shorthand
A syntax shorthand that lets you omit the type name when accessing constructors or static members, including enum values.
A dot shorthand is a language feature that allows for more concise code by omitting the type name when accessing constructors or static members, including enum values.
This feature is only allowed when the expected type of the expression can be uniquely inferred from the surrounding context (the context type).
For example, instead of writing
Status.running, you can write
.running if the context type is already
known to be Status:
enum Status { running, stopped }
void updateStatus(Status s) {
print('Status updated to: $s');
}
void main() {
updateStatus(.running); // Resolves to Status.running
}
Related docs and resources
Entrypoint
A Dart library that is directly invoked by a Dart implementation.
In the general context of Dart, an
entrypoint is a Dart library that is directly
invoked by a Dart implementation. For example, when
you pass a Dart library as a command-line argument to
the standalone Dart VM, that library is the
entrypoint. In other words, it's usually the
.dart file that contains
main().
In the context of pub, an entrypoint package or root package is the root of a dependency graph. It will usually be an application. When you run your app, it's the entrypoint package. Every other package it depends on will not be an entrypoint in that context.
A package can be an entrypoint in some contexts and
not in others. Say your app uses a package
A. When you run your app,
A is not the entrypoint package. However,
if you go over to A and execute its
tests, in that context, it is the entrypoint
since your app isn't involved.
Related docs and resources
Entrypoint directory
A directory that contains Dart entrypoints.
An entrypoint directory is a directory inside your Dart package that is allowed to contain Dart entrypoints.
Pub has a list of these directories:
benchmark, bin,
example, test,
tool, and web (and
lib, for
Flutter apps). Any subdirectories of those (except
bin) can also contain entrypoints.
Related docs and resources
Extension type
A compile-time wrapper that provides a zero-cost abstraction over an underlying type.
An extension type introduces a static, compile-time-only wrapper around an underlying representation type.
Because an extension type is erased at compile time, it imposes zero runtime memory or CPU cost while providing a distinct, type-safe API.
Related docs and resources
Function
An umbrella term to refer to top-level functions, local functions, static methods, and instance methods.
Related docs and resources
Getter
A special method that provides read access to an object's properties.
Getters and setters are special methods that provide
read and write access to an object's properties.
Recall that each instance variable has an implicit
getter, plus an implicit setter if appropriate. You
can create additional properties by implementing
getters and setters, using the get and
set keywords.
Related docs and resources
Immediate dependency
A dependency that a Dart package directly uses.
An immediate dependency is a
dependency that
a package directly uses and declares itself. The
dependencies you list in your
pubspec.yaml
file are your package's immediate dependencies. All
other dependencies are
transitive dependencies.
Related docs and resources
Immutable
An object whose state, including all nested values, can't be changed after it is created.
An immutable object is one whose state can't be
modified after it is created. When an object is
immutable, all of its fields must be
final (can't be reassigned), and the
values of those fields must themselves be immutable
(can't be mutated). This helps ensure consistency and
enables safer use in concurrent or reactive code.
In Dart, a class is immutable if you:
- Declare all fields as final, so they can't be reassigned.
- Ensure the field values themselves are immutable.
-
Optionally, use the
@immutableannotation from the meta package. This lets the analyzer warn you if any field is not final or refers to a mutable type.
Additionally, all Dart const values are immutable. For
example, const [1, 2, 3] creates an
immutable list. If a class has a
const (non-factory) constructor, then all
of its fields must be final.
Example:
import 'package:meta/meta.dart';
@immutable
class User {
final String name;
final int age;
const User(this.name, this.age);
}
In the proceeding example, once created, you can't
modify the User instance. You must create
a new one to change any data.
Related docs and resources
Instantiate
Creating a concrete instance (object) of a class.
Instantiating a class means creating an object of that
class's type in memory. In Dart, you do this by
calling a constructor, such as
final myItem = Item(). Abstract classes
can't be instantiated directly.
Related docs and resources
Interop
The ability for Dart code to interact with code written in other languages.
Interop (short for interoperability) refers to Dart's ability to communicate and work with code written in other programming languages.
In Dart, interop is commonly used to:
- Call native code written in languages like C, C++, or Rust.
- Interact with platform-specific APIs.
- Reuse existing libraries that aren't written in Dart.
Dart provides different interop mechanisms depending on the platform and use case. For example, Dart applications can use the Foreign Function Interface (FFI) to call native libraries, or platform-specific interop layers when running on Flutter or the web.
Related docs and resources
Irrefutable pattern
A pattern that always matches.
Irrefutable patterns are patterns that always match. Irrefutable patterns are the only patterns that can appear in irrefutable contexts: the declaration and assignment pattern contexts.
Related docs and resources
Just-in-Time compilation (JIT)
Just-in-Time compilation (JIT) is a compilation mode that compiles code at runtime for fast development iteration.
Just-in-Time (JIT) compilation compiles Dart code to native machine code as it runs. This enables features like hot reload and rich debugging by allowing for incremental compilation. JIT is typically used during development, whereas production builds uses AOT.
Related docs and resources
Late
A keyword that enables deferred initialization of variables and is typically used with non-nullable variables.
The late keyword in Dart is used to
indicate that a variable will be initialized later,
after its declaration, but before it's used. This
helps avoid the need to make a variable nullable
(?) when you know it will definitely
receive a value, just not immediately.
Using late defers initialization,
allowing you to write more flexible and readable code,
especially when dealing with dependencies or complex
setup.
For example:
late String description;
void setup() {
description = 'This will be initialized before use.';
}
Be careful with late variables that are part of a
public API. If a client accesses the variable before
it's initialized, they will encounter a
LateInitializationError, which provides
little context. In such cases, consider using a
private nullable variable with a public getter that
throws a descriptive error (e.g.,
StateError) if accessed too early as this
can offer clearer feedback to API users, despite the
added complexity.
You can also use late final when the
variable should only be set once. This is useful in
scenarios where the value is not available at object
construction time, such as cyclic dependencies in
object graphs.
Example:
class LinkedQueue<T> {
late final QueueLink<T> _head;
LinkedQueue() {
_head = QueueLink<T>._head(owner: this); // Cyclic reference between objects
}
}
Be cautious: if a late variable is accessed before it's initialized or never initialized at all, it will cause a runtime error.
Related docs and resources
Library
A single compilation unit in Dart, made up of a primary Dart file and its parts.
A Dart library is a single compilation unit
in Dart, made up of a primary .dart file
and any optional number of
parts. Libraries
have their own private scope.
Related docs and resources
Library-private
Accessibility restricted to the library where it is defined.
In Dart, privacy is scoped to the library rather than
the class. Any identifier prefixed with an underscore,
such as _myVariable, is library-private.
Related docs and resources
Lockfile
A file named pubspec.lock that specifies the versions of each dependency.
A file named pubspec.lock that specifies
the concrete versions and other identifying
information for every
immediate
and
transitive
dependency a package relies on.
Unlike the pubspec, which only lists immediate dependencies and allows version ranges, the lockfile comprehensively pins down the entire dependency graph to specific versions of packages. A lockfile ensures that you can recreate the exact configuration of packages used by an application.
The lockfile is generated automatically for you by pub
when you run
pub get,
pub upgrade, or
pub downgrade. Pub includes a
content hash
for each dependency to check against during future
resolutions.
If your package is an application package, you will typically check this into source control. For regular (library) packages, you usually won't.
Related docs and resources
Mixin application
A class created when a mixin is applied to a class.
A mixin application is the class created when a mixin is applied to a class. For example, consider the following declarations:
class A {}
mixin M {}
class B extends A with M {}
The class B is a subclass of the mixin
application of M to A,
sometimes nomenclated as A+M. The class
A+M is a subclass of A and
has members that are copied from M.
You can give an actual name to a mixin application by defining it as:
class A {}
mixin M {}
class A_M = A with M;
Given this declaration of A_M, the
following declaration of B is equivalent
to the declaration of B in the original
example:
class B extends A_M {}
Related docs and resources
Null safety
A feature of Dart's type system where variables can't
contain null unless their type explicitly
permits it.
Null safety in Dart is a language feature that helps
prevent errors caused by unintentional access of
null values.
With null safety, types are non-nullable by default: a
variable can't contain null unless you
declare its type as nullable with a
? suffix.
int count = 42; // Can't be null.
int? maybeCount; // Can be null (and is initialized to null by default).
Dart's null safety is
sound. If the type system determines that a variable has a
non-nullable type, that variable is guaranteed to
never be null at runtime. This soundness
guarantee is enforced through a combination of static
analysis and runtime checks, and it enables compiler
optimizations that produce smaller and more efficient
code.
Related docs and resources
Obviously typed
An expression whose type can be mechanically determined without ambiguity.
An expression in Dart is considered obviously typed when its type is immediately clear from its syntax, without needing inference or complex analysis.
This concept is mainly used in lint rules like
omit_obvious_local_variable_types and
specify_nonobvious_property_types
to decide whether an explicit type annotation is
redundant or necessary.
Obviously typed expressions
#The following types of expressions are considered obviously typed:
-
Non-collection literals:
1,true,'Hello'. -
Collection literals with explicit type arguments:
<int>[],<String, bool>{}. -
Homogeneous list/set literals whose elements are
obviously typed:
[1, 2, 3],{true, false}. -
Homogeneous map literals with obvious key and value
types:
{1: 10, 2: 20}. -
Record literals with obvious types:
(1, enabled: true). - Local variables and formal parameters that have not been promoted.
-
Instance creation with non-generic classes or
explicit type arguments:
C(),Set<int>(). -
Cascades with obviously typed targets:
StringBuffer('Hello, ')..write('world!'). -
Type casts:
x as int. -
Conditional expressions with obviously typed
branches of the same type:
condition ? 1 : 2. -
Type tests:
x is String. -
Throw expressions:
throw Exception(). -
Parenthesized expressions whose contents are
obviously typed:
('Hello!'). -
thisand type literals:String.
In this list, homogeneous means "where every
element has the same type". For example,
{1: 'one', 2: 'two'} is a homogeneous
map, but {1: 'one', 1.5: true} is not. By
contrast, if the type of an expression requires
inference (for example [1, 2.5] →
List<num>), it is
not
considered obviously typed.
This distinction helps style rules decide when to enforce or omit type annotations for clarity and consistency.
Override inference
How missing types in a method declaration are inferred.
Override inference is the process by which any missing types in a method declaration are inferred based on the corresponding types from the method or methods that it overrides.
If a candidate method (the method that's missing type information) overrides a single inherited method, then the corresponding types from the overridden method are inferred. For example, consider the following code:
class A {
int m(String s) => 0;
}
class B extends A {
@override
m(s) => 1;
}
The declaration of m in B is
a candidate because it's missing both the return type
and the parameter type. Because it overrides a single
method (the method m in A),
the types from the overridden method will be used to
infer the missing types and it will be as if the
method in B had been declared as
int m(String s) => 1;.
If a candidate method overrides multiple methods, and the function type one of those overridden methods, Ms, is a supertype of the function types of all of the other overridden methods, then Ms is used to infer the missing types. For example, consider the following code:
class A {
int m(num n) => 0;
}
class B {
num m(int i) => 0;
}
class C implements A, B {
@override
m(n) => 1;
}
The declaration of m in C is
a candidate for override inference because it's
missing both the return type and the parameter type.
It overrides both m in A and
m in B, so the compiler
needs to choose one of them from which the missing
types can be inferred. But because the function type
of m in A (int Function(num)) is a supertype of the function type of
m in B (num Function(int)), the function in A is used to infer
the missing types. The result is the same as declaring
the method in C as
int m(num n) => 1;.
It is an error if none of the overridden methods have a function type that is a supertype of all the other overridden methods.
Related docs and resources
Package
A directory with a collection of Dart libraries, resources, and a pubspec.yaml file describing them.
A Dart package is a collection of Dart
libraries and
resources in a directory, with a
pubspec.yaml
file in the root of that directory.
Packages can have
dependencies on
other packages and can be dependencies
themselves. A package's /lib directory
contains the
public libraries
that other packages can import and use. They can also
include scripts to be run directly. A package that is
not intended to be depended on by other packages is an
application package. Shared packages are
published
to pub.dev, but you can also have non-published
packages.
Don't check the lockfile of a package into source control, since libraries should support a range of dependency versions. The version constraints of a package's immediate dependencies should be as wide as possible while still ensuring that the dependencies will be compatible with the versions that were tested against.
Since
semantic versioning
requires that libraries increment their major version
numbers for any backwards incompatible changes,
packages will usually require their dependencies'
versions to be greater than or equal to the versions
that were tested and less than the next major version.
So if your library depended on the (fictional)
transmogrify package and you tested it at
version 1.2.1, your version constraint
would be ^1.2.1.
Related docs and resources
Package uploader
A pub.dev user who has administrative permissions for a package.
A package uploader is someone who has administrative permissions for a package. A package uploader can upload new versions of the package, and they can also add and remove other uploaders for that package.
If a package has a verified publisher, then all members of the publisher can upload the package.
Related docs and resources
Part file
A Dart source file that contains a
part of directive.
A part file is a Dart source file that contains a
part of directive and is included in a
library using the part directive.
Related docs and resources
Potentially constant
An expression that can be evaluated at compile time if all constructor parameters of the enclosing constant constructor are replaced by constant values, and all type parameters are replaced by concrete types.
A potentially constant expression is an expression that is valid in contexts where a constant is required, such as initializer lists of constant constructors, even though it might not be a compile-time constant itself because it references constructor parameters or type parameters.
If all constructor parameters of the enclosing constant constructor are replaced by constant values of the appropriate type, and all type parameters are replaced by concrete types, a potentially constant expression becomes a compile-time constant expression.
For example, in the following class:
class ConstPoint {
final int x;
final int y;
const ConstPoint(int a) : x = a, y = a + 1;
}
The initializing expressions a and
a + 1 are potentially constant
expressions. They are not constant expressions because
the value of a depends on how the
constructor is called.
However, if the constructor is invoked with
const ConstPoint(3), the parameter
a is replaced by the constant
3, making 3 and
3 + 1 constant expressions.
Related docs and resources
Potentially non-nullable
A type that is either non-nullable explicitly or due to being a type parameter.
A type is potentially non-nullable if it's either explicitly non-nullable or if it's a type parameter.
A type is explicitly non-nullable if it is a type name
that isn't followed by a question mark
(?). Note that there are a few types that
are always nullable, such as
Null and dynamic, and that
FutureOr is only non-nullable if it isn't
followed by a question mark and the type
argument is non-nullable (such as
FutureOr<String>).
Type parameters are potentially non-nullable because
the actual runtime type (the type specified as a type
argument) might be non-nullable. For example, given a
declaration of class C<T> {}, the
type C could be used with a non-nullable
type argument as in C<int>.
Related docs and resources
Primary constructor
A concise syntax to declare a main constructor directly in the header of a class, enum, or extension type.
A primary constructor is a syntax shorthand that declares a main constructor directly in the definition header of a class, enum, or extension type.
Primary constructors reduce the boilerplate of
parameter declarations and field initialization by
allowing declaring parameters (final or
var) and initializing formals
(this.field) directly in the header.
Declaring a parameter with the var or
final modifier implicitly creates and
initializes that field:
class Point(var int x, var int y);
Related docs and resources
Private named parameter
A named constructor parameter with a private name in the declaration and a public name at call sites.
A private named parameter is a named parameter whose name is private.
It is only allowed when the parameter is initializing
(such as this._x) or declaring (such as
final T _x or var T _x).
Initializing parameters occur in traditional or
primary constructors, while declaring parameters only
occur in primary constructors.
An initializing parameter initializes the instance variable with the given private name. A declaring parameter both implicitly declares and initializes an instance variable with the given private name.
At call sites, arguments are passed to private named parameters by specifying the corresponding public name (omitting the leading underscore):
class Point {
final double _x;
final double _y;
// Callers use the public names 'x' and 'y'.
Point.named({required this._x, required this._y});
}
void main() {
var p = Point.named(x: 10, y: 20); // Initializes '_x' and '_y'.
}
Related docs and resources
Pub content hash
SHA256 hashes maintained by pub.dev to validate package integrity.
The pub.dev repository maintains a SHA256 content hash of each version of each package it hosts. Pub clients use this hash to validate the integrity of downloaded packages, and protect against changes on the source repository.
When dart pub get downloads a package, it
computes the hash of the downloaded archive. The hash
of each hosted dependency is stored with the
resolution
in the lockfile.
The pub client uses this content hash to verify that
running dart pub get again using the same
lockfile, potentially on a different computer, uses
exactly the same packages.
If the locked hash doesn't match what's currently in the pub cache, pub redownloads the archive. If it still doesn't match, the lockfile updates and a warning is printed.
To make a discrepancy become an error instead of a
warning, use the
--enforce-lockfile
option for dart pub get. With this
option, if pub can't find package archives with the
same hashes, dependency resolution fails and the
lockfile isn't updated.
Related docs and resources
Pub system cache
A directory where pub stores downloaded remote packages.
When pub gets a remote package, it downloads it into a
single pub system cache directory. On macOS
and Linux, this directory defaults to
~/.pub-cache. On Windows, the directory
defaults to %LOCALAPPDATA%\Pub\Cache,
though its exact location might vary depending on the
Windows version. You can specify a different location
using the
PUB_CACHE
environment variable.
Once packages are in the system cache, pub creates a
package_config.json file that maps each
package used by your application to the corresponding
package in the cache.
You only have to download a given version of a package
once and can then reuse it in as many packages as you
would like. If you specify the
--offline flag to use cached packages,
you can delete and regenerate your
package_config.json
files without having to access the network.
Related docs and resources
Pub workspace
A collection of packages that are developed together with a shared resolution of their dependency constraints.
A pub workspace associated a collection of local packages that are treated as a single unit during development, enabling shared resolution of their dependency constraints. Useful for developing in a monorepo.
The packages have shared pubspec.lock and
.dart_tool/package_config.json files in
the workspace root directory.
Related docs and resources
Public library
A library that is located in a package's
lib directory but not inside the
lib/src directory.
A public library is a library that is located inside
the package's lib directory but not
inside the lib/src directory.
Related docs and resources
Quick fix
An automated, local code edit targeted at fixing the issue reported by a specific diagnostic.
Related docs and resources
Refactor
A code edit targeted at modifications that are either non-local or that require user interaction.
A refactor is a code edit targeted at modifications that are either non-local or that require user interaction. Examples of refactors include renaming, removing, or extracting code.
Related docs and resources
Refutable pattern
A pattern that can be tested against a value.
A refutable pattern is a pattern that can be tested against a value to determine if the pattern matches the value. If not, the pattern refutes, or denies, the match. Refutable patterns appear in matching contexts.
Related docs and resources
Reified generics
The preservation of generic type arguments at runtime,
enabling type checks against the full generic type,
such as with is List<String>.
In Dart, generic type arguments are reified, which means they keep track of their type arguments at runtime. This lets you inspect and check the full generic type of an object, not just its raw type without type arguments.
For example, you can check whether a list is
specifically a
subtype of
List<String>:
final names = ['Dart', 'Flutter', 'Jaspr'];
print(names is List<String>); // Prints: true
print(names is List<int>); // Prints: false
Reified generics also enable Dart's runtime type
checks for
covariant generic
types. To ensure
soundness, when you
add an element to a list, Dart validates at runtime
that the element matches the list's actual type
argument. For example, if you try to add a
double to a List<int>,
because double is not a subtype of
int, Dart throws an error at runtime:
List<num> numbers = <int>[1, 2, 3]; // Explicitly typed as `List<num>`.
numbers.add(1.5); // Throws a TypeError because `1.5` isn't an int.
This contrasts with some other programming languages,
such as Java, where type arguments are removed before
execution using type erasure. So in those
languages, while you can test whether an object is a
List, you can't test whether it's a
List<String>.
Related docs and resources
Scope
The region of a program where a name (such as a variable, function, or class) is visible and can be referenced.
Scope defines where identifiers—such as variables, parameters, functions, and classes are accessible within a program.
Dart uses lexical (static) scoping, meaning that the scope of a name is determined by the structure of the source code, not by how the program executes at runtime. See lexical scope for more details.
Common kinds of scope in Dart include:
- Class scope: Members accessible within a class.
-
Block scope: Variables declared
inside blocks such as
if,for, orwhile. - Function scope: Parameters and local variables inside a function.
- Library scope: Top-level declarations visible within a library.
Related docs and resources
Shadowing
When a local declaration hides another with the same name.
Shadowing occurs when a local declaration, such as a variable or parameter, uses the same name as an existing declaration in an outer scope, making the outer one inaccessible within the inner scope.
While valid in Dart, shadowing can lead to confusing code or unintended behavior. As a result, it's generally discouraged unless used deliberately to improve the clarity of your code.
Example
#
In this example, the local
message variable inside the
printMessage function
shadows the top-level
message variable:
final message = 'Global';
void printMessage() {
final message = 'Local'; // Shadows the global `message` variable.
print(message); // Prints: Local
}
void main() {
printMessage();
print(message); // Prints: Global
}
Shadowing can also occur in nested blocks:
void main() {
final value = 10;
if (true) {
final value = 20; // Shadows the outer `value` variable.
print(value); // Prints: 20
}
print(value); // Prints: 10
}
Sound
A guarantee from the type system that an expression's runtime value always matches its static type.
A type system is sound when it guarantees
that a program can never get into a state where an
expression evaluates to a value that doesn't match the
expression's static type. For example, if an
expression's static type is String, the
runtime value is guaranteed to be a
String when evaluated.
Dart's type system is sound. It enforces this guarantee through a combination of static checking (compile-time errors) and runtime checks:
-
Static checks catch most type
errors at compile time, such as assigning a
Stringto a variable with aninttype. -
Runtime checks handle cases the
compiler can't verify statically, such as casts with
asand covariant generic type arguments.
Soundness gives you several practical benefits:
- Type-related bugs surface at compile time. A sound type system forces code to be unambiguous about its types, so type-related bugs that might be tricky to find at runtime are caught early.
- More readable code. You can rely on a value actually having its declared type. In sound Dart, types can't lie.
- More maintainable code. When you change one piece of code, the sound type system enables Dart tooling to warn you about other code that broke.
- Better ahead-of-time (AOT) compilation. Sound types help the compiler generate smaller, more efficient native code.
Soundness doesn't mean that Dart catches every bug. You can still write code with logic errors, off-by-one mistakes, and other problems that the type system doesn't address. It just means that the types themselves are always trustworthy.
Related docs and resources
Subclass
A class that inherits the implementation of another class.
A subclass is a class that inherits the
implementation of another class by using the
extends
keyword, or by
mixin application.
// A is a subclass of B; B is the superclass of A.
class A extends B {}
// B1 has the superclass `A with M`, which has the superclass A.
class B1 extends A with M {}
A subclass relation also implies an associated
subtype relation.
For example, class A implicitly defines
an associated type A
which instances of the class A inhabit.
So, class A extends B declares not just
that the class A is a subclass of
B, but also establishes that the
type A is a subtype of
the type B.
Subclass relations are a subset of subtype relations.
When the documentation says "S must be a
subtype of T", it's fine for
S to be a subclass of T.
However, the converse is not true: not all subtypes
are subclasses.
Related docs and resources
Subtype
A type that can be used wherever a value of its supertype is expected.
A subtype relation is where a value of a
certain type is substitutable where the value of
another type, the supertype, is expected. For example,
if S is a subtype of T, then
you can substitute a value of type
S where a value of type T is
expected.
A subtype supports all of the operations of its supertype (and possibly some extra operations). In practice, this means you can assign the value of a subtype to any location expecting the supertype, and all of the methods of the supertype are available on the subtype.
This is true at least statically. A specific API might not allow the substitution at run time, depending on its operations.
Some subtype relations are based on the structure of
the type, like with nullable types (for example,
int is a subtype of int?)
and function types (for example,
String Function() is a subtype of
void Function()).
Subtypes can also be introduced for classes by implementation or inheritance (direct or indirect):
// A is a subtype of B, but NOT a subclass of B.
class A implements B {}
// C is a subtype AND a subclass of D.
class C extends D {}
Related docs and resources
Super parameter
A constructor parameter that automatically passes an argument to the superclass constructor.
A super parameter is a syntax shorthand that simplifies passing parameters from a subclass constructor up to a superclass constructor.
Instead of manually declaring the parameter and
passing it in the initializer list, you can prefix the
parameter name with super..
For example, in
Hammer({required super.price}), the
price value is passed along directly to
the superclass constructor.
Related docs and resources
Top type
A type that is a supertype of every other type,
including Object?.
A top type is any type that is a
supertype of Object?. Because
Object? is itself a supertype of every
other Dart type, a top type sits at the very top of
the type hierarchy, so you can assign any value to a
variable with a top type.
Dart has many top types, but the primary ones are the following three:
-
Object?- The nullable supertype of all types. Every value in Dart, includingnull, is anObject?. -
dynamic- A special type that disables static type checking. LikeObject?, it accepts any value, but it also allows you to access any member on it without a compile-time error. The check is deferred to runtime instead. -
void- A type that indicates its value is not intended to be used. You can assign any value tovoid, but you can't use the result without a cast.
Beyond these primary three, there are other composite
top types.
FutureOr<T> is a top type whenever
T is a top type, and adding
? to certain types produces a top type as
well. For example,
FutureOr<Object?>,
FutureOr<Object>?, and
dynamic? are all top types.
In contrast to Object?, the non-nullable
Object type is the supertype of all
non-nullable types but is not a true top type
because it isn't a supertype of Null.
The top type is the converse of the
bottom type.
While a top type is a supertype of everything, the
bottom type (Never in Dart) is a subtype
of everything.
Object? a = 42; // OK: int is a subtype of Object?.
Object? b = 'hello'; // OK: String is a subtype of Object?.
Object? c = null; // OK: Null is a subtype of Object?.
dynamic d = [1, 2, 3]; // OK: List<int> is a subtype of dynamic.
void e = true; // OK: bool is a subtype of void.
Transitive dependency
A dependency that a package indirectly uses because one of its dependencies requires it.
A transitive dependency is a dependency that a package indirectly uses because one of its dependencies, or their dependencies, requires it.
If your package depends on A, which in turn depends on B which depends on C, then A is an immediate dependency and B and C are transitive ones.
Related docs and resources
Tree shaking
A compiler optimization that removes unused code.
Tree shaking is a compiler optimization that analyzes which parts of the code are actually used by an application and removes everything else from the final output.
This makes the compiled program smaller and faster to load, since dead code that is never referenced doesn't end up in the binary or bundle.
Tree shaking is especially important for web and mobile apps, where code size directly impacts download time and runtime performance.
For example, if you import a library that defines ten top-level functions, but your app only calls two, tree shaking ensures the other eight functions are excluded from the compiled output.
Related docs and resources
Type alias
A user-defined name for an existing type.
A type alias is an alternative name that refers to another type.
They can be used to simplify complex type definitions, improve readability, or create semantic meaning in code.
Dart supports defining type aliases using the
typedef keyword. You can alias functions,
classes, and even generic types.
Examples
#Function type alias
#typedef StringTransformer = String Function(String);
void printTransformed(String input, StringTransformer transformer) {
print(transformer(input));
}
void main() {
printTransformed('hello', (str) => str.toUpperCase()); // Output: HELLO
}
Class alias
#class HttpClient {}
typedef Client = HttpClient;
Client client = HttpClient();
Type aliases don't create new types, they just provide alternate names.
Related docs and resources
Uninitialized
A variable that has been declared but has not yet been assigned a value.
A variable is uninitialized when it has been declared but has not yet been assigned a value.
Non-nullable variables must be initialized before they are read, either at the point of declaration or through all possible execution paths before the first use:
void f() {
int x;
print(x); // Error: variable 'x' must be assigned before it can be used.
}
Nullable variables that aren't explicitly initialized
default to null:
int? count; // Implicitly initialized to null.
print(count); // Prints: null
To defer initialization of a non-nullable variable,
use the late keyword. A
late variable is uninitialized until its
first assignment, and accessing it before it's
assigned throws a late initialization error at run
time:
late String name;
print(name); // Throws a late initialization error if not yet assigned.
Related docs and resources
Variance and variance positions
How changing a type argument of a type affects the relationship between the original type and the resulting one.
In Dart, changing the type argument of a type declaration (like a class) or function return type, changes the overall type relationship in the same direction (covariant).
However, changing the type of a function's parameter types, changes the overall type relationship in the opposite direction (contravariant).
A type parameter of a class (or other type declaration, like a mixin) is said to be covariant when the type as a whole "co-varies" with the actual type argument. In other words, if the type argument is replaced by a subtype then the type as a whole is also a subtype.
For example, the type parameter of the class
List is covariant because list types
co-vary with their type argument:
List<int> is a subtype of
List<Object> because
int is a subtype of Object.
In Dart, all type parameters of all class, mixin, mixin class, and enum declarations are covariant.
However, function types are different: A function type
is covariant in its return type, but the opposite
(known as contravariant) in its parameter
types. For example, the type
int Function(int) is a subtype of the
type Object Function(int), but it is a
supertype of int Function(Object).
This makes sense if you consider their
substitutability.
If you call a function with a static type of
int Function(int), that function can
actually be of type
int Function(Object) at runtime. Based on
the static type, you expect to be able to pass an
int to it. That will be fine since the
function actually accepts any Object, and
this includes every object of type int.
Similarly, the returned result will be of type
int, which is also what you expect based
on the static type.
Hence, int Function(Object) is a subtype
of int Function(int).
Note that everything is turned upside-down for
parameter types. In particular, this subtype relation
among function types requires that the
opposite subtype relation exists for the
parameter type. For example,
void Function(Object) is a subtype of
void Function(int)
because int is a subtype of
Object.
With a more complex type like
List<void Function(int)>, you have
to consider the positions in the type. To
accomplish this, turn one of the parts of the type
into a placeholder, and then consider what happens to
the type when different types are placed in that
position.
For example, consider
List<void Function(_)> as a
template for a type where you can put different types
in place of the placeholder _. This type
is contravariant in the position where that
placeholder occurs.
The following illustrates this by substituting
Object and int for
_.
List<void Function(Object)> is a
subtype of List<void Function(int)>
because void Function(Object) is a
subtype of void Function(int)
because
void is a subtype of
void (the return types) and
int is a subtype of
Object (the parameter types, in the
opposite order). Hence, the type at
_ varies in the opposite direction of the
type List<void Function(_)> as a
whole, and this 'opposite direction' by definition
makes it a contravariant position.
A covariant position is defined similarly.
For example, _ is at a covariant position
in the type List<_>, and
_ is also at a covariant position in the
type _ Function(int).
There is yet another kind of position known as invariant, but it occurs much more rarely so the details are omitted here.
In practice, it's often sufficient to know that the type arguments of a class, mixin, etc. are in a covariant position, and so is the return type of a function type, but the parameter types are in a contravariant position.
Related docs and resources
Verified publisher
A package publisher on the pub.dev site whose identity has been verified by pub.dev.
A verified publisher is a collection of one or more users who are identified with a unique domain name. The ownership of the domain name is verified by pub.dev, such as for the dart.dev publisher on pub.dev by the Dart team.
Related docs and resources
Version constraint
A constraint associated with each dependency that specifies which versions a package is expected to work with.
A version constraint is a specified range of
compatible versions of a
dependency for
a package. This can be a single version
(0.3.0) or a range of versions
(^1.2.1). While any is also
allowed, for performance reasons it's not recommended.
Library packages should always specify version constraints for each of their non-dev dependencies. Application packages, on the other hand, can allow any version of their dependencies, since they use the lockfile to manage their dependency versions.
Related docs and resources
WebAssembly (Wasm)
A binary instruction format that enables high-performance execution of code on web browsers.
WebAssembly (commonly abbreviated to Wasm) is a portable binary format designed as a compilation target for programming languages, enabling close-to-native execution speeds on the web.
Dart supports compiling code directly to WebAssembly
(using the
dart compile wasm tool). Compiling Dart
to Wasm can be especially beneficial for Flutter web
apps, improving frame rates, startup times, and
application size compared to standard JavaScript
compilation.
Related docs and resources
Wildcard
A symbol (_) used instead of a variable
name to indicate an unused value in patterns and other
contexts.
A wildcard is the underscore character
(_) used to ignore values or indicate
that a value is intentionally unused. It's often used
in patterns, destructuring, and switch expressions to
match any value without binding it to a name.
Wildcards help make the code more intentional by clearly marking values that aren't needed in a specific context.
Example:
// Ignoring the value in a for-each loop.
var names = ['Alice', 'Bob', 'Charlie'];
for (var _ in names) {
print('Someone is here!');
}
The wildcard pattern is particularly useful when:
- You only need certain parts of a destructured value.
- You want to explicitly show some values are being ignored.
- You need a catch-all case in pattern matching.
Related docs and resources
Zone
A mechanism to customize the behavior of asynchronous code without modifying the asynchronous code itself.
A zone is an execution context that allows you to run code with customized behavior for asynchronous events such as timers, microtasks, and uncaught errors.
Zones are useful for:
- Logging
- Error tracking
- Maintaining request-specific state across async gaps (for example, in server apps)
- Testing and debugging async behavior
Zones provide a way to track and influence asynchronous execution without requiring the asynchronous code to be aware of it.
You can create a new zone using
runZoned (or
runZonedGuarded) and override
zone-specific behavior such as error handling and
timers. Even print can be overridden,
although it's not asynchronous and just included for
convenience.
Example:
import 'dart:async';
void main() {
runZonedGuarded(() {
Future.delayed(Duration(seconds: 1), () {
throw 'Zone caught this error!';
});
}, (error, stackTrace) {
print('Caught error: $error');
});
}
In the preceding example, the uncaught error inside the async callback is intercepted by the custom zone.
Related docs and resources
除非另有说明,文档之所提及适用于 Dart 3.12.2 版本报告页面问题.