Dart language evolution
This page lists notable changes and additions to the Dart programming language.
- To learn specific details about the most recent supported language version, check out the language documentation or the language specification.
- For a full history of changes to the Dart SDK, see the SDK changelog.
To use a language feature introduced after 2.0, set an SDK constraint no lower than the release when Dart first supported that feature.
For example: To use null safety, introduced in 2.12,
set 2.12.0
as the lower constraint in the pubspec.yaml
file.
environment:
sdk: '>=2.12.0 <3.0.0'
Changes in each release
Dart 3.0
Released 10 May 2023 | Dart 3.0 announcement
Dart 3.0 introduced several new major language features:
- Patterns, a new category of grammar that lets you match and destructure values.
- Records, a new type that lets you aggregate multiple values of different types in a single function return.
- Class modifiers, a new set of keywords that let you control how a class or mixin can be used.
Dart 2.19
Released 25 January 2023
Dart 2.19 introduced some precautions surrounding type inference. These include:
- More flow analysis flags for unreachable code cases.
- No longer delegate inaccessible private names to
noSuchMethod
. - Top-level type inference throws on cyclic dependencies.
Dart 2.19 also introduced support for unnamed libraries. Library directives, used for appending library-level doc comments and annotations, can and should now be written without a name:
/// A really great test library.
@TestOn('browser')
library;
Dart 2.18
Released 30 August 2022 | Dart 2.18 announcement
Dart 2.18 enhanced type inference. This change allows information flow between arguments in generic function calls. Before 2.18, if you didn’t specify an argument’s type in some methods, Dart reported errors. These type errors cited potential null occurrences. With 2.18, the compiler infers the argument type from other values in an invocation. You don’t need to specify the argument type inline.
Dart 2.18 also discontinued support for mixin classes that don’t extend
Object
.
To learn more about these features, check out:
Dart 2.17
Released 11 May 2022 | Dart 2.17 announcement
Dart 2.17 expanded enum functionality with enhanced enums. Enhanced enums allow enum declarations to define members including fields, constructors, methods, getters, etc.
Dart 2.17 added support for super-initializer parameters in constructors. Super parameters allow you to avoid having to manually pass each parameter into the super invocation of a non-redirecting constructor. You can instead use super parameters to forward parameters to a superclass constructor.
Dart 2.17 removed some restrictions on named arguments. Named arguments can now be freely interleaved with positional arguments. As of Dart 2.17, you can write the following code:
void main() {
test(skip: true, 'A test description', () {
// Very long function body here...
});
}
To learn more about these features, check out:
Dart 2.16
Released 3 February 2022 | Dart 2.16 announcement
Dart 2.16 added no new features to the Dart language. It did expand the Dart tools.
Dart 2.15
Released 8 December 2021 | Dart 2.15 announcement
Dart 2.15 improved support for function pointers, known as tear-offs. In particular, constructor tear-offs are now supported.
Dart 2.14
Released 8 September 2021 | Dart 2.14 announcement
Dart 2.14 added the unsigned shift (or triple-shift) operator (>>>
).
This new operator works like >>
,
except that it always fills the most significant bits with zeros.
To learn more about these operators, check out bitwise and shift operators.
Dart 2.14 removed some restrictions on type arguments. You can pass type arguments to annotations and use a generic function type as a type argument. As of Dart 2.14, you can write the following code:
@TypeHelper<int>(42, "The meaning")
late List<T Function<T>(T)> idFunctions;
var callback = [<T>(T value) => value];
late S Function<S extends T Function<T>(T)>(S) f;
Dart 2.13
Released 19 May 2021 | Dart 2.13 announcement
Dart 2.13 expanded support for type aliases (typedef
).
Type aliases used to work only for function types
but now work for any type.
You can use the new name created with a type alias
anywhere the original type could be used.
Dart 2.13 improved the struct support in Dart FFI, adding support for inline arrays and packed structs.
Dart 2.12
Released 3 March 2021 | Dart 2.12 announcement
Dart 2.12 added support for sound null safety. When you opt into null safety, types in your code are non-nullable by default, meaning that variables can’t contain null unless you say they can. With null safety, your runtime null-dereference errors turn into edit-time analysis errors.
In Dart 2.12, Dart FFI graduated from beta to the stable channel.
Dart 2.10
Released 1 October 2020 | Dart 2.10 announcement
Dart 2.10 added no new features to the Dart language.
Dart 2.9
Released 5 August 2020
Dart 2.9 added no new features to the Dart language.
Dart 2.8
Released 6 May 2020 | Dart 2.8 announcement
Dart 2.8 didn’t add any features to the Dart language. It did contain a number of preparatory breaking changes to improve nullability-related usability and performance for null safety.
Dart 2.7
Released 11 December 2019 | Dart 2.7 announcement
Dart 2.7 added support for extension methods, enabling you to add functionality to any type —-even types you don’t control—- with the brevity and auto-complete experience of regular method calls.
The following example extends the String
class from
dart:core
with a new parseInt()
method:
extension ParseNumbers on String {
int parseInt() {
return int.parse(this);
}
}
void main() {
int i = '42'.parseInt();
print(i);
}
Dart 2.6
Released 5 November 2019 | Dart 2.6 announcement
Dart 2.6 introduced a
breaking change (dart-lang/sdk#37985).
Constraints where Null
serves as a subtype of FutureOr<T>
now yield Null
as the solution for T
.
For example: The following code now prints Null
.
Before Dart 2.6, it printed dynamic
.
The anonymous closure () {}
returns the Null
type.
import 'dart:async';
void foo<T>(FutureOr<T> Function() f) { print(T); }
main() { foo(() {}); }
Dart 2.5
Released 10 September 2019 | Dart 2.5 announcement
Dart 2.5 didn’t add any features to the Dart language, but it did add
support for calling native C code from Dart code
using a new core library, dart:ffi
.
Dart 2.4
Released 27 June 2019
Dart 2.4 introduces a breaking change dart-lang/sdk#35097.
Dart now enforces covariance of type variables used in super-interfaces. For example: Prior to this release Dart accepted, but now rejects, the following code:
class A<X> {};
class B<X> extends A<void Function(X)> {};
You can now use async
as an identifier in
asynchronous and generator functions.
Dart 2.3
Released 8 May 2019 | Dart 2.3 announcement
Dart 2.3 added three operators designed to improve code that performs list manipulation, such as declarative UI code.
The spread operator
enables unpacking the elements from one list into another.
In the following example, the list returned by buildMainElements()
is unpacked into the list being passed to the children
argument:
Widget build(BuildContext context) {
return Column(children: [
Header(),
...buildMainElements(),
Footer(),
]);
}
The collection if operator enables adding elements conditionally.
The following example adds a FlatButton
element unless
the app displays the last page:
Widget build(BuildContext context) {
return Column(children: [
Text(mainText),
if (page != pages.last)
FlatButton(child: Text('Next')),
]);
}
The collection for operator enables building repeated elements.
The following example adds one HeadingAction
element for
each section in sections
:
Widget build(BuildContext context) {
return Column(children: [
Text(mainText),
for (var section in sections)
HeadingAction(section.heading),
]);
}
Dart 2.2
Released 26 February 2019 | Dart 2.2 announcement
Dart 2.2 added support for set literals:
const Set<String> currencies = {'EUR', 'USD', 'JPY'};
Dart 2.1
Released 15 November 2018 | Dart 2.1 announcement
Dart 2.1 added support for int-to-double conversion,
allowing developers to set double
values using integer literals.
This feature removed the annoyance of being forced to use a
double
literal (for example, 4.0
)
when the value was an integer in concept.
In the following Flutter code, horizontal
and vertical
have type double
:
padding: const EdgeInsets.symmetric(
horizontal: 4,
vertical: 8,
)
Dart 2.0
Released 22 February 2018 | Dart 2.0 announcement
Dart 2.0 implemented a new sound type system. Before Dart 2.0, types weren’t fully sound, and Dart relied heavily on runtime type checking. Dart 1.x code had to be migrated to Dart 2].
Language versioning
A single Dart SDK can simultaneously support multiple versions of the Dart language. The compiler determines what version the code is targeting, and it interprets the code according to that version.
Language versioning becomes important on the rare occasions when Dart introduces an incompatible feature like null safety. When Dart introduces a breaking change, code that did compile might no longer compile. Language versioning allows you to set each library’s language version to maintain compatibility.
In the case of null safety, Dart SDKs 2.12 through 2.19 allowed you to choose to update your code to use null safety. Dart uses language versioning to permit non-null-safe code to run alongside null-safe code. This decision enabled migration from non-null-safe to null-safe code. To review an example of how an app or package can migrate to a new language version with an incompatible feature, check out Migrating to null safety.
Each package has a default language version equal to the lower bound
of the SDK constraint in the pubspec.yaml
file.
For example: The following entry in a pubspec.yaml
file
indicates that this package defaults to the Dart 2.18 language version.
environment:
sdk: '>=2.18.0 <3.0.0'
Language version numbers
Dart formats its language versions as two numbers separated with a period. It reads as a major version number and a minor version number. Minor version numbers might introduce breaking changes.
Dart releases might append a patch number to a language version. Patches should not change the language except for bug fixes. To illustrate: Dart 2.18.3 serves as the latest release of the Dart 2.18 SDK language version.
Each Dart SDK supports all of the language versions within its major version number. That means that Dart SDK 2.18.3 supports language versions 2.0 through 2.18 inclusive, but not Dart 1.x.
Deriving the language version from the SDK version implies the following:
-
Whenever a minor version of the SDK ships, a new language version appears. In practice, many of these language versions work in a very similar manner to previous versions and have with full compatibility between them. For example: The Dart 2.9 language works much like the Dart 2.8 language.
-
When a patch release of the SDK ships, it cannot introduce new language features. For example: The 2.18.3 release remains language version 2.18. It must remain compatible with 2.18.2, 2.18.1, and 2.18.0.
Per-library language version selection
By default, every Dart file in a package uses the same language version.
Dart identifies the default language version as the
lower-bound of the SDK constraint specified in the pubspec.yaml
file.
Sometimes, a Dart file might need to use an older language version.
For example, you might not be able to migrate all the files in a package
to null safety at the same time.
Dart supports per-library language version selection. To opt to have a different language version from the rest of a package, a Dart library must include a comment in the following format:
// @dart = <major>.<minor>
For example:
// Description of what's in this file.
// @dart = 2.17
import 'dart:math';
...
The @dart
string must be in a //
comment (not ///
or /*
),
and it must appear before any Dart code in the file.
Whitespace (tabs and spaces) doesn’t matter,
except within the @dart
and version strings.
As the previous example shows,
other comments can appear before the @dart
comment.
To learn how and why the Dart team developed this versioning method, check out the language versioning specification.