Dart 3 migration guide
Dart 3 is a major release that introduces new core capabilities to Dart: records, patterns, and class modifiers.
Alongside these new capabilities, Dart 3 contains a number of changes that may break existing code.
This guide will help you resolve any migration issues you might encounter after upgrading to Dart 3.
Introduction
Unversioned vs versioned changes
The potentially breaking changes listed below fall into one of two categories:
-
Unversioned changes: These changes affect any Dart code after upgrading to a Dart 3.0 SDK or later. There is no way to “turn off” these changes.
-
Versioned changes: These changes only apply when the package or app’s language version is set to >= Dart 3.0. The language version is derived from the
sdk
lower-constraint in thepubspec.yaml
file. An SDK constraint like this does not apply the Dart 3 versioned changes:environment: sdk: '>=2.14.0 <3.0.0'
But an SDK constraint like this does:
environment: sdk: '>=3.0.0 <4.0.0'
To use the new Dart 3 features you have to update the language version to 3.0. This gets you the Dart 3 versioned changes at the same time.
Dart 3 backwards compatibility
Many packages and apps that used null safety with Dart 2.12 or later are likely backwards compatible with Dart 3. This is possible for any package where the lower bound of the SDK constraint is 2.12.0 or higher.
Dart’s pub tool allows resolution even when
the upper bound is limited to versions below 3.0.0.
For example, a package with the following constraint
will be allowed to resolve with a Dart 3.x SDK,
as pub will re-interpret the upper-constraint <3.0.0
as <4.0.0
when the lower constraint is 2.12
or higher:
environment:
sdk: '>=2.14.0 <3.0.0' # This is interpreted as '>=2.14.0 <4.0.0'
This allows developers to use Dart 3 sound null safety with packages that already support 2.12 null safety without needing a second migration, unless the code is affected by any other Dart 3 changes.
Testing for impact
To understand if your source code is impacted by any Dart 3 changes, use these steps:
$ dart --version # Make sure this reports 3.0.0 or higher.
$ dart pub get # This should resolve without issues.
$ dart analyze # This should pass without errors.
If the pub get
step fails, try to upgrade your dependencies
to see if more recent versions might support Dart 3:
$ dart pub upgrade
$ dart analyze # This should pass without errors.
Or, if needed, also include major versions upgrades:
$ dart pub upgrade --major-versions
$ dart analyze # This should pass without errors.
Dart 3 language changes
100% sound null safety
Dart 2.12 introduced null safety more than two years ago. In Dart 2.12, users needed to enable null safety with a pubspec setting. In Dart 3, null safety is built in; you cannot turn it off.
Scope
This is an unversioned change, that applies to all Dart 3 code.
Symptom
Packages developed without null safety support will cause issues
when resolving dependencies with pub get
:
$ dart pub get
Because pkg1 doesn't support null safety, version solving failed.
The lower bound of "sdk: '>=2.9.0 <3.0.0'" must be 2.12.0 or higher to enable null safety.
Libraries that opt out of null safety with language version comments
that select any language version below 2.12
will
cause analysis or compilation errors:
$ dart analyze .
Analyzing .... 0.6s
error • lib/pkg1.dart:1:1 • The language version must be >=2.12.0.
Try removing the language version override and migrating the code.
• illegal_language_version_override
$ dart run bin/my_app.dart
../pkg1/lib/pkg1.dart:1:1: Error: Library doesn't support null safety.
// @dart=2.9
^^^^^^^^^^^^
Migration
Before beginning any migration to Dart 3,
ensure your app or package has been 100% migrated to enable null safety.
This requires a Dart 2.19
SDK, not a Dart 3 SDK.
To learn how to first migrate your app or package to support null safety,
check out the null safety migration guide.
Colon-syntax for default values
For historical reasons, named optional parameters could
specify their default value using either :
or =
.
In Dart 3, only the =
syntax is allowed.
Scope
This is a versioned change, that only applies to language version 3.0 or later.
Symptom
Dart analysis produces errors like:
line 2 • Using a colon as a separator before a default value is no longer supported.
Migration
Change from using colons:
int someInt({int x: 0}) => x;
To using equals:
int someInt({int x = 0}) => x;
This migration can be made manually, or automated with dart fix
:
$ dart fix --apply --code=obsolete_colon_for_default_value
mixin
Pre-Dart 3, any class
could be used as a mixin
, as long as
it had no declared constructors and no superclass other than Object
.
In Dart 3, classes declared in libraries at language version 3.0 or later
can’t be used as mixins unless marked mixin
.
This restriction applies to code in any library
attempting to use the class as a mixin,
regardless of the latter library’s language version.
Scope
This is a versioned change, that only applies to language version 3.0 or later.
Symptom
An analysis error like:
Mixin can only be applied to class.
The analyzer produces this diagnostic when a class that is neither a
mixin class
nor a mixin
is used in a with
clause.
Migration
Determine if the class is intended to be used as a mixin.
If the class defines an interface, consider using implements
.
switch
Dart 3.0 interprets switch cases as patterns instead of constant expressions.
Scope
This is a versioned change, that only applies to language version 3.0 or later.
Symptom
Most constant expressions found in switch cases are valid patterns with the same meaning (named constants, literals, etc.). These will behave the same and no symptoms will arise.
The few constant expressions that aren’t valid patterns
will trigger the invalid_case_patterns
lint.
Migration
You can revert back to the original behavior by prefixing
the case pattern with const
, so it’s no longer interpreted as a pattern:
case const [1, 2]:
case const {'k': 'v'}:
case const {1, 2}:
case const Point(1, 2):
You can run a quick fix for this breaking change,
by using dart fix
or from your IDE.
continue
Dart 3 reports a compile-time error if a continue statement targets a label
that is not a loop (for
, do
, and while
statements) or a switch member.
Scope
This is a versioned change, that only applies to language version 3.0 or later.
Symptom
You will see an error like:
The label used in a 'continue' statement must be defined on either a loop or a switch member.
Migration
If changing behavior is acceptable,
change the continue
to target a valid labeled statement,
which must be attached to a for
, do
or while
statement.
If you want to preserve behavior, change the
continue
statement to a break
statement.
In previous versions of Dart, a continue
statement
that wasn’t targeted at a loop or a switch member
behaved like break
.
Dart 3 core library changes
APIs removed
Breaking change #49529: The core libraries have been cleaned up to remove APIs that have been deprecated for several years. The following APIs no longer exist in the Dart core libraries.
Scope
This is an unversioned change, that applies to all Dart 3 code.
dart:core
- Removed the deprecated
List
constructor, as it wasn’t null safe. Use list literals (e.g.[]
for an empty list or<int>[]
for an empty typed list) orList.filled
. This only impacts non-null safe code, as null safe code already couldn’t use this constructor. - Removed the deprecated
onError
argument onint.parse
,double.parse
, andnum.parse
. Use thetryParse
method instead. - Removed the deprecated
proxy
andProvisional
annotations. The originalproxy
annotation has no effect in Dart 2, and theProvisional
type andprovisional
constant were only used internally during the Dart 2.0 development process. - Removed the deprecated
Deprecated.expires
getter. UseDeprecated.message
instead. - Removed the deprecated
CastError
error. UseTypeError
instead. - Removed the deprecated
FallThroughError
error. The kind of fall-through previously throwing this error was made a compile-time error in Dart 2.0. - Removed the deprecated
NullThrownError
error. This error is never thrown from null safe code. - Removed the deprecated
AbstractClassInstantiationError
error. It was made a compile-time error to call the constructor of an abstract class in Dart 2.0. - Removed the deprecated
CyclicInitializationError
. Cyclic dependencies are no longer detected at runtime in null safe code. Such code will fail in other ways instead, possibly with a StackOverflowError. - Removed the deprecated
NoSuchMethodError
default constructor. Use theNoSuchMethodError.withInvocation
named constructor instead. - Removed the deprecated
BidirectionalIterator
class. Existing bidirectional iterators can still work, they just don’t have a shared supertype locking them to a specific name for moving backwards.
dart:async
- Removed the deprecated
DeferredLibrary
class. Use thedeferred as
import syntax instead.
dart:developer
- Removed the deprecated
MAX_USER_TAGS
constant. UsemaxUserTags
instead. - Removed the deprecated
Metrics
,Metric
,Counter
, andGauge
classes as they have been broken since Dart 2.0.
dart:html
- As previously announced, the deprecated
registerElement
andregisterElement2
methods inDocument
andHtmlDocument
have been removed. See #49536 for details.
dart:math
- The
Random
interface can only be implemented, not extended.
dart:io
- Update
NetworkProfiling
to accommodate newString
ids that are introduced in vm_service:11.0.0
Symptom
Dart analysis (e.g. in your IDE, or in dart analyze
/flutter analyze
)
will fail with errors like:
error line 2 • Undefined class 'CyclicInitializationError'.
Migration
Manually migrate away from using these APIs.
Extends & implements
Dart 3 supports new class modifiers that can restrict the capabilities of a class. They have been applied to a number of classes in the core libraries.
Scope
This is a versioned change, that only applies to language version 3.0 or later.
dart:async
-
The following declarations can only be implemented, not extended:
StreamConsumer
StreamIterator
StreamTransformer
MultiStreamController
None of these declarations contained any implementation to inherit. They are marked as
interface
to signify that they are only intended as interfaces.
dart:core
-
The
Function
type can no longer be implemented, extended or mixed in. Since Dart 2.0, writingimplements Function
has been allowed for backwards compatibility, but it has not had any effect. In Dart 3.0, theFunction
type is final and cannot be subtyped, preventing code from mistakenly assuming it works. -
The following declarations can only be implemented, not extended:
Comparable
Exception
Iterator
Pattern
Match
RegExp
RegExpMatch
StackTrace
StringSink
None of these declarations contained any implementation to inherit. They are marked as
interface
to signify that they are only intended as interfaces. -
The following declarations can no longer be implemented or extended:
MapEntry
OutOfMemoryError
StackOverflowError
Expando
WeakReference
Finalizer
The
MapEntry
value class is restricted to enable later optimizations. The remaining classes are tightly coupled to the platform and not intended to be subclassed or implemented.
dart:collection
-
The following interface can no longer be extended, only implemented:
Queue
-
The following implementation classes can no longer be implemented:
LinkedList
LinkedListEntry
-
The following implementation classes can no longer be implemented or extended:
-
HasNextIterator
(Also deprecated.) HashMap
LinkedHashMap
HashSet
LinkedHashSet
DoubleLinkedQueue
ListQueue
SplayTreeMap
SplayTreeSet
-
Dart 3 tools changes
Removed tools
Historically the Dart team has offered a number of smaller developer tools for
things like formatting code (dartfmt
), analyzing code (dartanalyzer
), etc.
In Dart 2.10 (October 2020) we introduced a new unified Dart developer tool, the
dart
tool.
Scope
This is an unversioned change, that applies to all Dart 3 code.
Symptom
In Dart 3 these smaller tools do not exist, and
have been replaced by the new combined dart
tool.
Migration
Use new sub-commands available in the dart
tool:
Historical tool |
dart replacement |
Deprecation | Discontinuation |
---|---|---|---|
stagehand |
dart create |
2.14 | 2.14* |
dartfmt |
dart format |
2.14 | 2.15 |
dart2native |
dart compile exe |
2.14 | 2.15 |
dart2js |
dart compile js |
2.17 | 2.18 |
dartdevc |
webdev |
2.17 | 2.18 |
dartanalyzer |
dart analyze |
2.16 | 2.18 |
dartdoc |
dart doc |
2.16 | 2.17 |
pub |
dart pub |
2.15 | 2.17 |
Null safety migration tools
The following null safety migration commands have been removed, as Dart 3 doesn’t support code without null safety:
dart migrate
dart pub upgrade --null-safety
dart pub outdated --mode=null-safety
Scope
This is an unversioned change, that applies to all Dart 3 code.
Symptom
These commands will fail.
Migration
Use Dart 2.19 to migrate to null safety.
Analyzer config
The analyzer configuration options for enabling stricter checking have changed.
Scope
This is an unversioned change, that applies to all Dart 3 code.
Symptom
The former configuration options will fail with a warning like:
The option 'implicit-casts' is no longer supported.
Try using the new 'strict-casts' option.
Migration
Replace this part of the analyzer config:
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
with:
analyzer:
language:
strict-casts: true
strict-raw-types: true
Other tools changes
- The deprecated Observatory has been hidden by default. We recommend using DevTools.
- The command
dart format fix
has been replaced bydart fix
#1153. - The snapshot files bundled in the SDK for the Dart web compiler have been cleaned up #50700.
- The output of
dart format
changed a bit for some code. - Ending backwards compatibility for the old location of pub-cache on Windows.
Prior to Dart 3
%APPDATA%\Pub\Cache
was a fallback location for pub-cache. Starting with Dart 3, the default pub-cache is located at%LOCALAPPDATA%\Pub\Cache
. If you have added globally activated packages to yourPATH
, consider updatingPATH
to contain%LOCALAPPDATA%\Pub\Cache\bin
.
Scope
This is an unversioned change, that applies to all Dart 3 code.