Dart (3.13) Module 19 – Staying current and preparing for interviews
What changed from Dart 3.0 to 3.13
Every Dart language change from 3.0 to 3.13 and the version it needs, a recorded upgrade of a Dart 2.19 package, and the outdated advice to check in tutorials.
What you will learn
- List the language change of each release from Dart 3.0 to 3.13 and the lower bound it needs
- Predict the error a file gets when it uses a feature below the language version that introduced it
- Move a Dart 2.19 package to the current language version with pub, dart fix and dart format
- Identify outdated advice in older tutorials by testing it on the current SDK
Before you start
What you’ll learn · Before you start
What you will learn
- List the language change of each release from Dart 3.0 to 3.13 and the lower bound it needs
- Predict the error a file gets when it uses a feature below the language version that introduced it
- Move a Dart 2.19 package to the current language version with pub, dart fix and dart format
- Identify outdated advice in older tutorials by testing it on the current SDK
Before you start
On this page
A Dart 2.19 package with sdk: '>=2.19.0 <3.0.0' still compiles and runs on the 3.13.5 SDK, and dart analyze finds only
six hints in it. Raise the lower bound to 3.0 and the same code gets two errors. Every Dart 3 release works like that: the
SDK is new, but each package opts in to a language version, and the feature list you may use is decided by that number.
This lesson lists what each release from 3.0 to 3.13 added to the language, proves each feature by compiling it at its own version and one below, upgrades a small Dart 2.19 package step by step, and tests four things older tutorials still say. Every output was recorded with Dart 3.13.5.
Each language change needs a language version, set by the SDK constraint
The language version: The version of the Dart language a library is read with, written as major.minor, such as 3.13. A package's default is the lower bound of its SDK constraint. More in “What Dart is and where it runs” of a package is the lower bound of its SDK constraint: The environment sdk entry of pubspec.yaml, such as ^3.13.0: the Dart SDK versions a package works with. Its lower bound also sets the package's language version. More in “What Dart is and where it runs”. sdk: ^3.8.0 means that the code is
compiled with the rules of language 3.8, and a feature that arrived in 3.10 is not available to it, however new the
installed SDK is. An older constraint is not an error either. The Dart 3 migration guide explains that pub reads an upper
bound of <3.0.0 as <4.0.0, so old packages keep resolving, and that is why the Dart 2.19 package above still ran. A
single file can also set its own version with a first-line comment such as // @dart=3.8, which the next section uses.
The table gives every release from 3.0 to 3.13 with the lower bound that its language change needs:
| Version | Language change | Lower bound |
|---|---|---|
| 3.0 | Records, patterns, switch expressions, class modifiers; null safety is mandatory | ^3.0.0 |
| 3.2 | Private final fields can be promoted by null checks and is tests |
^3.2.0 |
| 3.3 | Extension types; abstract getters promote like private final fields | ^3.3.0 |
| 3.4 | Sharper types for conditional, if-null and switch expressions | ^3.4.0 |
| 3.6 | Digit separators: 1_000_000 |
^3.6.0 |
| 3.7 | Wildcard variables, inference using bounds, and the tall style of dart format |
^3.7.0 |
| 3.8 | Null-aware elements: [?lunch] |
^3.8.0 |
| 3.9 | Promotion, reachability and definite assignment assume null safety | ^3.9.0 |
| 3.10 | Dot shorthands: .green for Color.green |
^3.10.0 |
| 3.11 | No language changes | none |
| 3.12 | Private named parameters; primary constructors as an experiment | ^3.12.0 |
| 3.13 | Primary constructors; new or factory in place of the class name in other constructors; a tightened promotion rule |
^3.13.0 |
Releases 3.1 and 3.5 changed no syntax. Walk the same list release by release, with what each change does:
- 3.0
Records, patterns, switch expressions, class modifiers new syntax
The release that made null safety mandatory also added records, pattern matching, switch expressions and class modifiers. A switch case is now a pattern, and a case needs no break.
- 3.2
Private final field promotion flow analysis
A private final field can be promoted by a null check or an is test, so if (_name != null) lets you use _name without a bang.
- 3.3
Extension types new syntax
An extension type wraps an existing type with a different static interface and is erased at run time, so it costs no wrapper object.
- 3.4
Sharper conditional types type analysis
Type analysis of conditional, if-null and switch expressions improved. A few cases changed their inferred types, which the changelog lists as breaking.
- 3.6
Digit separators new syntax
Underscores may group the digits of a number literal, as in 1_000_000. They do not change the value.
- 3.7
Wildcard variables and the tall style new syntax
A local variable or parameter named _ no longer binds a name, so it can be declared many times. Inference using bounds arrived, and dart format gained the tall style for packages at 3.7 or later.
- 3.8
Null-aware elements new syntax
A leading question mark in a collection literal, as in [?lunch], adds the value only when it is not null.
- 3.9
Null safety assumed in analysis flow analysis
Type promotion, reachability and definite assignment now assume sound null safety, so some code gets more accurate results and more dead-code warnings.
- 3.10
Dot shorthands new syntax
Where the context type is known, .green stands for Color.green, and .new for a constructor, so the type name is not repeated.
- 3.11
No language changes tools only
The release changed tools and libraries only, so no package needs a new lower bound for it.
- 3.12
Private named parameters new syntax
A named parameter can initialise a private field, as in Point({required this._x}), and callers still write x. Primary constructors arrived as an experiment.
- 3.13
Primary constructors new syntax
A class header can declare its constructor and fields, as in class Point(final int x, final int y);. Constructors in the class body may write new or factory instead of the class name, and a promotion rule was tightened to avoid unsound behaviour.
The timeline has 12 events, from 3.0 to 3.13.
Read the table as a ladder. A package that wants dot shorthands must set ^3.10.0 or higher, and by doing so it also gets
every earlier rule, including the ones that broke old code, such as the removal of the colon before a default value in
3.0. That is why an upgrade goes one rung at a time.
Key idea
The SDK you install and the language version you compile with are two different numbers, and the second one comes from
the lower bound of the SDK constraint in pubspec.yaml.
Checkpoint
Check: Language version and SDK constraint
A feature fails one version below its own
The claim that a feature needs version X is easy to check. Each of eight small files starts with a comment that sets the version that introduced its feature, and the script runs the file as written and then again with that first line lowered by one version. The files use records (3.0), extension types (3.3), digit separators (3.6), wildcard variables (3.7), null-aware elements (3.8), dot shorthands (3.10), private named parameters (3.12) and primary constructors (3.13):
from session import files, run
# Eight features, each in a file whose first line sets the language version that introduced it. A second run lowers
# that line by one version.
files("ladder/records.dart=records.dart", "ladder/extension_type.dart=extension_type.dart", "ladder/digits.dart=digits.dart", "ladder/wildcard.dart=wildcard.dart", "ladder/null_aware_element.dart=null_aware_element.dart", "ladder/dot_shorthand.dart=dot_shorthand.dart", "ladder/private_named.dart=private_named.dart", "ladder/primary.dart=primary.dart", "ladder/ladder.sh=ladder.sh")
run(
"""
sh ladder.sh records 2.19
sh ladder.sh extension_type 3.2
sh ladder.sh digits 3.5
sh ladder.sh wildcard 3.6
sh ladder.sh null_aware_element 3.7
sh ladder.sh dot_shorthand 3.9
sh ladder.sh private_named 3.11
sh ladder.sh primary 3.12
""",
) Output
$ sh ladder.sh records 2.19 records as written: x=1.5 y=2.5 records at 2.19: lower.dart:2:1: Error: This requires the experimental 'records' language feature to be enabled. $ sh ladder.sh extension_type 3.2 extension_type as written: 42m extension_type at 3.2: lower.dart:2:11: Error: The 'inline-class' language feature is disabled for this library. $ sh ladder.sh digits 3.5 digits as written: 1002500 digits at 3.5: lower.dart:2:22: Error: This requires the experimental 'digit-separators' language feature to be enabled. $ sh ladder.sh wildcard 3.6 wildcard as written: xxx wildcard at 3.6: lower.dart:4:7: Error: '_' is already declared in this scope. $ sh ladder.sh null_aware_element 3.7 null_aware_element as written: [tea, water] null_aware_element at 3.7: lower.dart:5:10: Error: This requires the experimental 'null-aware-elements' language feature to be enabled. $ sh ladder.sh dot_shorthand 3.9 dot_shorthand as written: Color.green dot_shorthand at 3.9: lower.dart:5:13: Error: This requires the experimental 'dot-shorthands' language feature to be enabled. $ sh ladder.sh private_named 3.11 private_named as written: Point(1, 2) private_named at 3.11: lower.dart:4:24: Error: The 'private-named-parameters' language feature is disabled for this library. $ sh ladder.sh primary 3.12 primary as written: 7 primary at 3.12: lower.dart:2:12: Error: The 'primary-constructors' language feature is disabled for this library.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 ladder_session.py
Every file ran as written and failed one version below, and the message is worth reading. For records, digit separators,
null-aware elements and dot shorthands it said that the feature needs an experimental flag, which is how the compiler
names a feature that is not yet part of the language at that version. For extension types, private named parameters and
primary constructors it said that the language feature is disabled for this library; the next line of each message, cut
from the run above, asks for a language version of 3.3, 3.12 or 3.13 or higher. The wildcard file failed in a different way: at 3.6 the second var _ was a redeclaration of the first.
So when a teammate on an older constraint sees “language feature is disabled”, the fix is in the lower bound, not in the
code.
The first-line comment is a test tool
// @dart=3.8 applies to one file and is useful for experiments like this one. A real package sets the version once, in
pubspec.yaml, so that all its files agree.
Checkpoint
Check: A feature fails below its own version
Upgrading a Dart 2 package one step at a time
A small shop package is written the way Dart 2.19 code was: a plain class used as a mixin, a colon before a default
value, new everywhere, this. in front of members, and three statements laid out in the old style. The session moves
it from 2.19 to 3.13 in two steps. The first step raises the lower bound to 3.0, the second to 3.13:
upgrade/pubspec.yaml
name: shop
description: A small shop written in the style of Dart 2.19.
version: 1.0.0
publish_to: none
environment:
sdk: '>=2.19.0 <3.0.0' upgrade/analysis_options.yaml
linter:
rules:
- unnecessary_new
- unnecessary_const
- prefer_collection_literals
- prefer_final_fields
- unnecessary_this lib/shop.dart · upgrade/lib/shop.dart
class Logger {
void log(String message) => print('log: $message');
}
class Shop with Logger {
final items = new Map<String, int>();
final int limit;
Shop({this.limit : 3});
void add(String name, int quantity) {
if (this.items.length >= limit) {
log('shop is full, skipping $name');
return;
}
this.items[name] = quantity;
}
String describe(Object? value) {
if (value is List) {
return 'a list of ${value.length}';
} else if (value is Map) {
return 'a map of ${value.length}';
} else if (value == null) {
return 'nothing';
}
return 'something else';
}
} lib/report.dart · upgrade/lib/report.dart
String summary(Map<String, int> stock, int limit, String shop) {
final label = stock.length > limit ? 'overstocked at $shop with ${stock.length} lines' : stock.isEmpty ? 'empty at $shop' : 'normal at $shop';
final total = stock.values.fold<int>(0, (sum, quantity) => sum + quantity) + stock.length * 2 + limit * 3 + shop.length;
return label + ', ' + total.toString() + ' units counted';
}
List<String> longNames(List<String> names) {
final shouted = ['asha', 'ravi', 'meena'].map((name) => name.toUpperCase()).where((name) => name.length > 3).toList();
return shouted;
} bin/main.dart · upgrade/entry/main.dart
import 'package:shop/report.dart';
import 'package:shop/shop.dart';
void main() {
final shop = new Shop(limit: 2);
shop.add('tea', 4);
shop.add('rice', 10);
shop.add('salt', 1);
print(shop.describe(shop.items));
print(shop.describe([1, 2, 3]));
print(shop.describe(null));
print(summary(shop.items, 1, 'Pune'));
print(longNames(['x']));
} from session import files, run
# A package written for Dart 2.19, moved up in steps: first as it is, then to 3.0, then to 3.13.
files("upgrade/pubspec.yaml=pubspec.yaml", "upgrade/analysis_options.yaml=analysis_options.yaml", "upgrade/lib=lib", "upgrade/entry=bin")
run(
"""
dart pub get --offline | tail -1
dart run bin/main.dart
dart analyze | tail -n +3 | cut -c1-110
# step 1: raise the lower bound to 3.0, then pub get so that the language version follows
sed "s/sdk: '>=2.19.0 <3.0.0'/sdk: ^3.0.0/" pubspec.yaml > p.tmp && mv p.tmp pubspec.yaml
dart analyze | tail -1
dart pub get --offline | tail -1
dart analyze | grep error | cut -c1-120
dart fix --apply | tail -1
sed 's/^class Logger/mixin Logger/' lib/shop.dart > s.tmp && mv s.tmp lib/shop.dart
dart analyze | tail -1
dart run bin/main.dart > run.txt
head -2 run.txt
# step 2: raise it to 3.13 and format with the style that goes with it
dart format lib | tail -1
cp -R lib lib-before
sed "s/sdk: ^3.0.0/sdk: ^3.13.0/" pubspec.yaml > p.tmp && mv p.tmp pubspec.yaml
dart pub get --offline | tail -1
dart format lib | tail -1
diff -u lib-before/report.dart lib/report.dart | tail -n +3
""",
pub=True,
) Output
$ dart pub get --offline | tail -1
Got dependencies!
$ dart run bin/main.dart
log: shop is full, skipping salt
a map of 2
a list of 3
nothing
overstocked at Pune with 2 lines, 25 units counted
[ASHA, RAVI, MEENA]
$ dart analyze | tail -n +3 | cut -c1-110
info - bin/main.dart:5:16 - Unnecessary 'new' keyword. Try removing the 'new' keyword. - unnecessary_new
info - lib/shop.dart:6:17 - Unnecessary 'new' keyword. Try removing the 'new' keyword. - unnecessary_new
info - lib/shop.dart:6:17 - Unnecessary constructor invocation. Try using a collection literal. - prefer_co
info - lib/shop.dart:9:20 - Using a colon as the separator before a default value is deprecated and will no
info - lib/shop.dart:12:9 - Unnecessary 'this.' qualifier. Try removing 'this.'. - unnecessary_this
info - lib/shop.dart:16:5 - Unnecessary 'this.' qualifier. Try removing 'this.'. - unnecessary_this
6 issues found.
# step 1: raise the lower bound to 3.0, then pub get so that the language version follows
$ sed "s/sdk: '>=2.19.0 <3.0.0'/sdk: ^3.0.0/" pubspec.yaml > p.tmp && mv p.tmp pubspec.yaml
$ dart analyze | tail -1
6 issues found.
$ dart pub get --offline | tail -1
Got dependencies!
$ dart analyze | grep error | cut -c1-120
error - lib/shop.dart:5:17 - The class 'Logger' can't be used as a mixin because it's neither a mixin class nor a mixi
error - lib/shop.dart:9:20 - Using a colon as the separator before a default value is no longer supported. Try replaci
$ dart fix --apply | tail -1
5 fixes made in 2 files.
$ sed 's/^class Logger/mixin Logger/' lib/shop.dart > s.tmp && mv s.tmp lib/shop.dart
$ dart analyze | tail -1
No issues found!
$ dart run bin/main.dart > run.txt
$ head -2 run.txt
log: shop is full, skipping salt
a map of 2
# step 2: raise it to 3.13 and format with the style that goes with it
$ dart format lib | tail -1
Formatted 2 files (1 changed) in … seconds.
$ cp -R lib lib-before
$ sed "s/sdk: ^3.0.0/sdk: ^3.13.0/" pubspec.yaml > p.tmp && mv p.tmp pubspec.yaml
$ dart pub get --offline | tail -1
Got dependencies!
$ dart format lib | tail -1
Formatted 2 files (1 changed) in … seconds.
$ diff -u lib-before/report.dart lib/report.dart | tail -n +3
@@ -2,9 +2,10 @@
final label = stock.length > limit
? 'overstocked at $shop with ${stock.length} lines'
: stock.isEmpty
- ? 'empty at $shop'
- : 'normal at $shop';
- final total = stock.values.fold<int>(0, (sum, quantity) => sum + quantity) +
+ ? 'empty at $shop'
+ : 'normal at $shop';
+ final total =
+ stock.values.fold<int>(0, (sum, quantity) => sum + quantity) +
stock.length * 2 +
limit * 3 +
shop.length;
@@ -12,9 +13,10 @@
}
List<String> longNames(List<String> names) {
- final shouted = ['asha', 'ravi', 'meena']
- .map((name) => name.toUpperCase())
- .where((name) => name.length > 3)
- .toList();
+ final shouted = [
+ 'asha',
+ 'ravi',
+ 'meena',
+ ].map((name) => name.toUpperCase()).where((name) => name.length > 3).toList();
return shouted;
}
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 upgrade_session.py
Follow the order of the run in the next figure:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The old package still runs. The SDK reads the upper bound <3.0.0 as <4.0.0, and the language version stays at 2.19, so the code ran as before and the analyzer only reported deprecations.
- Raise the lower bound to 3.0. The language version follows the lower bound, but only once dart pub get rewrites package_config.json; straight after the edit the analyzer still reported the 6 issues it found at 2.19. After pub get it reports two errors, the colon it had flagged as deprecated and a plain class used as a mixin.
- Mechanical edits with dart fix. dart fix --apply made 5 fixes in 2 files, removing new, the this. qualifiers and a constructor call that a literal replaces, and replacing a colon default value with an equals sign.
- Fix what no tool covers. Logger was a plain class used as a mixin, which Dart 3 refuses. Declaring it with mixin was a one-word edit by hand, and the analyzer then reported no issues.
- Raise the lower bound to 3.13. With the bound at 3.13 and a fresh pub get, new language features are available in the package, and the formatter looks at the language version of each file.
- Format with the matching style. Language 3.7 or later gets the tall style. Three statements in report.dart changed shape, and no other file changed, so a style change makes a small diff of its own.
Three points from the run. The package ran and passed analysis with only hints before anything changed, so an upgrade is
a decision, not an emergency. Straight after the bound moved to 3.0, dart analyze still reported 6 issues, the count
it gave at 2.19, because the language version is read from the package configuration that dart pub get writes, not from pubspec.yaml
directly. Only after pub get ran again did the analyzer report two errors:
Logger cannot be used as a mixin, and the colon before the default value is no longer supported. dart fix --apply then made five fixes in two files, which covered the colon and the style hints
but not the mixin: one word, mixin Logger, by hand. Finally, after the bound moved to 3.13, dart format reshaped three statements of report.dart into the tall style: The code style that dart format applies to files at language version 3.7 or later. It adds and removes trailing commas itself, so long calls and chains break into short lines., and no other file changed. Commit the style change on its own, so that
the diff of the real upgrade stays small.
Climb in steps
Move to 3.0 first and get a clean dart analyze, then to the current version. Each step has few errors, and each
can be merged alone.
Checkpoint
Check: Upgrading a Dart 2 package in steps
Outdated advice to test before you trust it
Tutorials written for Dart 2 are still easy to find, and some of what they say stopped being true. A quick run on the
current SDK settles many of the claims. This session checks four of them: that Dart has method overloading, that a
switch case needs break, that a stream can be listened to only once unless it is a broadcast stream, and that
dart migrate upgrades a package to null safety:
from session import files, run
# Four things older tutorials say, checked against the Dart 3.13 SDK. dart migrate writes its reply to a file, so its
# status is its own and not that of a pipe that head may close early.
files("advice/overload.dart=overload.dart", "advice/switch_cases.dart=switch_cases.dart", "advice/streams.dart=streams.dart")
run(
"""
dart run overload.dart 2>&1 | head -2
dart run switch_cases.dart
dart run streams.dart
!dart migrate > migrate.txt 2>&1
head -1 migrate.txt
""",
) Output
$ dart run overload.dart 2>&1 | head -2 overload.dart:3:10: Error: 'greet' is already declared in this scope. String greet(String name, String title) => 'Hello, $title $name'; $ dart run switch_cases.dart one two still two other $ dart run streams.dart first listener: [1, 2, 3] second listener: [1, 2, 3] $ dart migrate > migrate.txt 2>&1 [exit status 64] $ head -1 migrate.txt Could not find a command named "migrate".
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 advice_session.py
Dart never had overloading: the second greet is an error because the name is already declared, and optional or named
parameters are the Dart way. A switch case in Dart 3 ends by itself, and several statements in one case run without a
break. The stream claim is only half true: a stream built by Stream.fromIterable was listened to twice and gave the
same list both times, so do not write that every stream that is not a broadcast stream can be listened to only once.
Finally dart migrate is gone, because null-safety migration is over and Dart 3 is sound null safety only.
Other lines to be careful with: dart:html and dart:js are deprecated, package:js is discontinued, and dart2wasm
refuses all three while dart2js still compiles them, as the lesson on web interop shows. Macros are not coming: the Dart team’s post on the subject says that it
stopped its work on them. Experimental syntax, such as the primary constructors that 3.12 offered as a preview, needs an
experiment flag: A command-line option such as --enable-experiment=name that turns on a language feature before it is released. Code that needs a flag can change or break between SDK releases. like dart run --enable-experiment=primary-constructors. The experiment-flags documentation warns against
experiments in production code, since they can change or disappear without notice. Keep them out of published packages.
Checkpoint
Check: Testing advice from older tutorials
Practise: bring a package up to date
Take any small package you have, or copy the shop package, and move it up in steps. Run each command and read what it says before you go on.
- Run
dart pub get,dart analyzeand the program with the constraint exactly as it is, and write down the hints. - Raise the lower bound to
^3.0.0, rundart pub get, and list every error the analyzer reports. - Run
dart fix --dry-runto see what it proposes, thendart fix --apply, then fix the remaining errors by hand. - Adopt a record or a pattern in one place where a function returns two values or tests a type, and keep the tests green.
- Raise the bound to your SDK’s version, run
dart pub get, thendart format ., and commit the formatting alone. - Look at
dart pub outdatedand move one dependency at a time to its latest major version.
Practice that adapts to you
Practise this lesson
10 questions about 4 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher to 2-5 years): Which Dart version introduced records and patterns, and what does a package have to do to use them?
Dart 3.0 introduced both, along with switch expressions and class modifiers. A package uses them by raising the lower
bound of its SDK constraint to 3.0, as in sdk: ^3.0.0, and running dart pub get so that the language version follows.
Installing a newer SDK is not enough, because the language version of a package comes from that lower bound. A file in a
package that is still at language 2.19 gets an error that names the feature, such as records, as one that needs an
experimental flag. Raising the bound also brings every rule that arrived with 3.0, including the removal of the colon before
a default value and the rule that only a class declared with mixin or mixin class can be used as a mixin, so run the
analyzer right after the change.
Two questions from the module’s bank go further, into planning a real upgrade and into checking what older documents say:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
A package with 80 files and sdk '>=2.19.0 <3.0.0' must reach the current language version within a week. Give the order of the steps, say what runs automatically and what needs a person, and how you know each step is safe to merge.
-
Mid to senior
You inherit a wiki that says switch needs break, dart migrate upgrades a package to null safety, and macros arrive soon. How do you check each claim against the current SDK, and what do you write in its place?
Flashcards · 7 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Where does a package's language version come from?
From the lower bound of its SDK constraint in pubspec.yaml, read through the package configuration that dart pub get writes. A newer SDK compiles an old package at its old language version.
-
In which versions did records and patterns, extension types, digit separators and wildcard variables arrive?
Records and patterns in 3.0, extension types in 3.3, digit separators in 3.6 and wildcard variables in 3.7.
-
In which versions did null-aware elements, dot shorthands, private named parameters and primary constructors arrive?
Null-aware elements in 3.8, dot shorthands in 3.10, private named parameters in 3.12 and primary constructors in 3.13.
-
What happens when code uses a feature below the language version that introduced it?
It fails with a message that names the feature, and the cure is raising the lower bound of the SDK constraint. A // @dart=x.y comment changes the version of one file, which is useful for experiments.
-
How do you upgrade a Dart 2.19 package?
In steps: raise the lower bound to 3.0, run pub get, let dart fix --apply do the mechanical work and fix the rest by hand, then raise it to the current version and format in a commit of its own.
-
Which old tutorial claims does the current SDK disprove?
Overloading never existed, break is not needed at the end of a switch case, Stream.fromIterable can be listened to twice, and dart migrate is gone.
-
Why do language experiments stay out of production code?
They sit behind flags and can change without notice, so they stay out of production code and published packages.
Key takeaways
- The language version of a package is the lower bound of its SDK constraint, read through the package configuration that
dart pub getwrites, and a new SDK compiles old packages at their old version. - Each release added at most a few language features: records and patterns in 3.0, extension types in 3.3, digit separators in 3.6, wildcard variables in 3.7, null-aware elements in 3.8, dot shorthands in 3.10, private named parameters in 3.12 and primary constructors in 3.13.
- A feature used below its version fails with a message that names it, and the cure is the lower bound.
- Upgrade in steps: raise to 3.0, run
pub get, letdart fix --applydo the mechanical work, fix the rest by hand, then raise to the current version and format in a commit of its own. - Test a claim from an old tutorial on the current SDK before you rely on it: overloading never existed,
breakis not needed,Stream.fromIterablecan be listened to twice, anddart migrateis gone. - Experiments sit behind flags and can change without notice, so they stay out of production code and published packages.
Terms in this lesson
- experiment flag
- A command-line option such as --enable-experiment=name that turns on a language feature before it is released. Code that needs a flag can change or break between SDK releases.
- tall style
- The code style that dart format applies to files at language version 3.7 or later. It adds and removes trailing commas itself, so long calls and chains break into short lines.
References
- Dart SDK changelog (The Dart project (Google))
- Dart language evolution (The Dart project (Google))
- Language versioning (The Dart project (Google))
- Dart 3 migration guide (The Dart project (Google))
- Breaking changes and deprecations (The Dart project (Google))
- Experiment flags (The Dart project (Google))
- Announcing Dart 3.13 (The Dart project (Google))
- Announcing Dart 3.12 (The Dart project (Google))
- An update on Dart macros and data serialization (The Dart project (Google))
Report a problem with this lesson
Kept only in this browser. Your Learn progress