Watch the video (10 min)
On this page24 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is a Dot Shorthand?
  5. 2. The Context Type
  6. 3. Arguments, Defaults, and Return Values
  7. 4. Switch Expressions and Switch Cases
  8. 5. Comparing with == and !=
  9. 6. Static Members
  10. 7. Constructors: .new and Named Constructors
  11. 8. Where It Does Not Work: No Context Type
  12. 9. A Chain Must End in the Context Type
  13. 10. Which Dart Version Added It?
  14. 11. Readability: When to Use It
  15. 12. A Real-World Example
  16. Code Examples
  17. Common Beginner Mistakes
  18. Best Practices
  19. Mini Challenge
  20. Practice Questions
  21. Practice Answers
  22. Quick Revision
  23. Key Takeaways
  24. What's Next?

Lecture 129 — Dot Shorthands

Category22 — Modern Dart Features
Lecture129 of 140
Estimated video length10 min
TargetDart 3.12.2
LevelAdvanced

When Dart already knows which type a value must have, writing Size.small repeats a name Dart can work out for itself. A dot shorthand lets you write just .small.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 025–128:

  • switch expressions and patterns in switch (Lectures 025 and 081)
  • named, const, and factory constructors (Lectures 054–055)
  • static members (Lecture 058)
  • enums and enhanced enums (Lectures 067–068)
  • Duration (Lecture 111)
  • language versions and the SDK constraint (Lecture 128)

Connection With Previous Lectures

Here is a coffee-size enum from Lecture 067, with a switch that prices each size:

dart
int price(Size size) => switch (size) {
  Size.small => 250,
  Size.medium => 350,
  Size.large => 450,
};

The parameter already says size is a Size. Every arm still repeats Size. anyway. In a longer switch, the type name is most of what you read. In Lecture 128 we saw that each new Dart version adds features. Dart 3.10 added one aimed exactly at this repetition.


Learning Objectives

By the end of this lecture you will be able to:

  • Understand what a dot shorthand is and what a context type is
  • Use .value for enum values in variables, arguments, defaults, and return values
  • Write switch cases and switch arms with dot shorthands
  • Compare with == and != using a shorthand on the right
  • Reach static members and constructors with .name, .new(), and .named()
  • Recognize the places where no context type exists, and their errors
  • Name the Dart version that introduced the feature
  • Decide when a shorthand helps readability and when it hurts

1. What Is a Dot Shorthand?

A dot shorthand is a name that starts with a dot, such as .small. Dart fills in the type name for you.

Analogy — at a coffee counter you say "a small, please", not "a small-size coffee". The cashier knows you are talking about cup sizes, so the word "size" is not needed.

dart
enum Size { small, medium, large }

void main() {
  Size cup = .small;
  print(cup);
}
output
Size.small

.small means exactly Size.small. The value is the same; only the code is shorter.


2. The Context Type

Dart can only fill in the name when it knows which type is expected at that spot. That expected type is called the context type. In Size cup = .small;, the declared type Size is the context, so Dart looks for small inside Size.

If the name does not exist in that type, Dart tells you which type it searched:

dart
enum Size { small, medium, large }

void main() {
  Size cup = .tiny; // ✗
  print(cup);
}
output
Error: The static getter or field 'tiny' isn't defined for the type 'Size'.

3. Arguments, Defaults, and Return Values

A typed parameter is a context type, and so is a declared return type.

dart
enum Size { small, medium, large }

void order({Size size = .medium}) => print('Ordered: ${size.name}');

Size biggest() => .large;

void main() {
  order();
  order(size: .small);
  order(size: biggest());
}
output
Ordered: medium
Ordered: small
Ordered: large

The default value, the argument, and the return value all use the shorthand.


4. Switch Expressions and Switch Cases

The value being switched on gives every arm its context, so the opening example shrinks:

dart
enum Size { small, medium, large }

int price(Size size) => switch (size) {
  .small => 250,
  .medium => 350,
  .large => 450,
};

void main() {
  print(price(.medium));
}
output
350

Switch statements work the same way, including || between values. The switch is still exhaustive: forget a value and Dart still names it.

dart
enum Size { small, medium, large }

void main() {
  Size cup = .large;
  switch (cup) {
    case .small:
      print('Sip');
    case .medium || .large:
      print('Big cup');
  }
}
output
Big cup

5. Comparing with == and !=

In a == b, the left side gives the context for the right side.

dart
enum Size { small, medium, large }

void main() {
  Size cup = .small;
  print(cup == .small);
  print(cup != .large);
}
output
true
true

Turn it around and there is nothing to the left to give a type:

dart
enum Size { small, medium, large }

void main() {
  Size cup = .small;
  print(.small == cup); // ✗
}
output
Error: No type was provided to find the dot shorthand 'small'.
  print(.small == cup);
         ^^^^^

Keep the variable on the left: cup == .small.


6. Static Members

Dot shorthands are not only for enums. Any static field, getter, or method of the context type can be reached the same way.

dart
void main() {
  Duration wait = .zero;
  int count = .parse('42');
  print(wait);
  print(count);
}
output
0:00:00.000000
42

.zero means Duration.zero, and .parse('42') means int.parse('42'). Your own static const fields work too, as the next step shows.


7. Constructors: .new and Named Constructors

.new(...) calls the unnamed constructor. A named constructor, including a factory, is .name(...). With a const constructor you can write const in front as usual.

dart
class Point {
  final int x;
  final int y;
  const Point(this.x, this.y);
  const Point.origin() : x = 0, y = 0;
  factory Point.fromJson(Map<String, int> json) =>
      Point(json['x']!, json['y']!);

  static const Point unit = Point(1, 1);

  @override
  String toString() => '($x, $y)';
}

void main() {
  Point a = .new(2, 3);
  const Point b = .origin();
  Point c = .fromJson({'x': 4, 'y': 5});
  Point d = .unit;
  print('$a $b $c $d');
}
output
(2, 3) (0, 0) (4, 5) (1, 1)

8. Where It Does Not Work: No Context Type

var and final without a type take their type from the value — but .small needs a type before it can become a value. Neither side can start:

dart
enum Size { small, medium, large }

void main() {
  var cup = .small; // ✗
  print(cup);
}
output
Error: No type was provided to find the dot shorthand 'small'.
  var cup = .small;
             ^^^^^

A context of Object, Object?, or dynamic is no better. print takes an Object?, so Dart searches the wrong type:

dart
enum Size { small, medium, large }

void main() {
  print(.small); // ✗
}
output
Error: The static getter or field 'small' isn't defined for the type 'Object?'.

9. A Chain Must End in the Context Type

You may continue after a shorthand, but the lookup still happens in the context type.

dart
void main() {
  int fee = .parse('-5').abs();
  print(fee);
}
output
5

Here int.parse gives an int, and abs() keeps it an int. This fails, because the context is String, so Dart searches String for small:

dart
enum Size { small, medium, large }

void main() {
  String label = .small.name; // ✗
  print(label);
}
output
Error: The static getter or field 'small' isn't defined for the type 'String'.

Write Size.small.name instead.


10. Which Dart Version Added It?

Dot shorthands arrived in Dart 3.10. Pin a file to 3.9 with the comment from Lecture 128 and the feature is switched off:

dart
// @dart=3.9
enum Size { small, medium, large }

void main() {
  Size cup = .small;
  print(cup);
}
output
Error: This requires the experimental 'dot-shorthands' language feature to be enabled.
Try passing the '--enable-experiment=dot-shorthands' command line option.

In a package, an SDK constraint of ^3.9.0 causes the same error. dart analyze gives the real fix: "Try updating your pubspec.yaml to set the minimum SDK constraint to 3.10.0 or higher, and running 'pub get'."


11. Readability: When to Use It

A shorthand removes a name. That is good when the type is obvious nearby — a switch on size, a parameter called size:, a declaration that starts with Size. It is bad when the reader must hunt for the type:

dart
void main() {
  Duration timeout = .new(seconds: 30); // the type is right there: fine
  print(timeout);
}
output
0:00:30.000000

Compare apply(.fast, .low, .on): without opening apply, nobody knows what those three values belong to. Spell out the type when the call site alone does not make it clear.


12. A Real-World Example

dart
enum Size {
  small(250),
  medium(350),
  large(450);

  final int price;
  const Size(this.price);
}

int total(List<Size> cups, {Size free = .small}) {
  int sum = 0;
  for (Size cup in cups) {
    if (cup != free) sum += cup.price;
  }
  return sum;
}

void main() {
  List<Size> order = [.large, .small, .medium];
  print('Total: ${total(order)}');
  print('Total: ${total(order, free: .large)}');
}
output
Total: 800
Total: 600

The list type gives each element its context, the parameter gives the default and the argument theirs, and cup != free compares two variables, so nothing needs a shorthand there.


Code Examples

Example 1 — An Enum Value in a Variable

dart
enum Level { beginner, intermediate, advanced }

void main() {
  Level ali = .intermediate;
  print(ali.name);
}
output
intermediate

Example 2 — An Argument

dart
enum Level { beginner, intermediate, advanced }

void enroll(String name, Level level) => print('$name: ${level.name}');

void main() {
  enroll('Sara', .advanced);
}
output
Sara: advanced

Example 3 — A Switch Expression

dart
enum Level { beginner, intermediate, advanced }

void main() {
  Level zayan = .beginner;
  int lessons = switch (zayan) {
    .beginner => 40,
    .intermediate => 25,
    .advanced => 10,
  };
  print(lessons);
}
output
40

Example 4 — Comparing

dart
enum Level { beginner, intermediate, advanced }

void main() {
  Level sara = .advanced;
  if (sara == .advanced) print('Ready for the project');
}
output
Ready for the project

Example 5 — A Static Constant

dart
void main() {
  Duration elapsed = .zero;
  elapsed += const Duration(minutes: 15);
  print(elapsed.inMinutes);
}
output
15

Example 6 — Constructors

dart
void main() {
  DateTime start = .new(2026, 3, 1);
  DateTime parsed = .parse('2026-03-15');
  print(parsed.difference(start).inDays);
}
output
14

.new(...) calls the DateTime constructor; .parse finds the static method DateTime.parse.

Example 7 — Student Marks

dart
enum Grade { a, b, c, fail }

Grade gradeFor(int marks) => switch (marks) {
  >= 80 => .a,
  >= 65 => .b,
  >= 50 => .c,
  _ => .fail,
};

void main() {
  print('Ali: ${gradeFor(84).name}');
  print('Sara: ${gradeFor(47).name}');
}
output
Ali: a
Sara: fail

The return type Grade is the context for every arm's result.

Example 8 — Shopping Total

dart
enum Size {
  small(250),
  large(450);

  final int price;
  const Size(this.price);
}

void main() {
  List<Size> cart = [.small, .large, .large];
  int total = 0;
  for (Size item in cart) {
    total += item.price;
  }
  print('Total: $total');
}
output
Total: 1150

Example 9 — Bank Balance

dart
enum Kind { deposit, withdrawal }

int apply(int balance, Kind kind, int amount) => switch (kind) {
  .deposit => balance + amount,
  .withdrawal => balance - amount,
};

void main() {
  int balance = 1000;
  balance = apply(balance, .deposit, 500);
  balance = apply(balance, .withdrawal, 200);
  print('Balance: $balance');
}
output
Balance: 1300

Example 10 — Temperature Conversion

dart
enum Unit { celsius, fahrenheit }

String show(double celsius, {Unit unit = .celsius}) => switch (unit) {
  .celsius => '$celsius °C',
  .fahrenheit => '${celsius * 9 / 5 + 32} °F',
};

void main() {
  print(show(25));
  print(show(25, unit: .fahrenheit));
}
output
25.0 °C
77.0 °F

Common Beginner Mistakes

1. Using var or final without a type

dart
var cup = .small;
output
Error: No type was provided to find the dot shorthand 'small'.

Write the type: Size cup = .small;.

2. Putting the shorthand on the left of ==

dart
if (.small == cup) print('small');
output
Error: No type was provided to find the dot shorthand 'small'.

The left side gives the context. Write cup == .small.

3. Passing a shorthand where the type is Object?

dart
print(.small);
output
Error: The static getter or field 'small' isn't defined for the type 'Object?'.

4. Expecting .values to give a list

dart
List<Size> all = .values;
output
Error: The static getter or field 'values' isn't defined for the type 'List<Size>'.

The context is List<Size>, so Dart searches List. Write Size.values.

5. Using the feature with an older language version

A pubspec with sdk: ^3.9.0 reports "This requires the 'dot-shorthands' language feature to be enabled." Raise the minimum to 3.10.0.


Best Practices

  • Use shorthands where the type is visible nearby: switches, typed declarations, named arguments.
  • Spell out the type when a bare .fast or .on would leave the reader guessing.
  • Keep the variable on the left of == and !=.
  • Prefer .new(...) only when the declared type is on the same line.
  • Be consistent inside one file — do not mix Size.small and .small in the same switch.

Mini Challenge

Write enum Light { red, yellow, green } and a function String action(Light light) that returns 'Stop', 'Wait', or 'Go' using a switch expression with dot shorthands. Call it with .green and with .red.

output
Go
Stop

Try it yourself before opening the solution.

Show solution
dart
enum Light { red, yellow, green }

String action(Light light) => switch (light) {
  .red => 'Stop',
  .yellow => 'Wait',
  .green => 'Go',
};

void main() {
  print(action(.green));
  print(action(.red));
}
output
Go
Stop

Practice Questions

Beginner

  1. Declare enum Color { red, green, blue }, store .green in a Color variable, and print it.
  2. Rewrite Duration wait = Duration(seconds: 30); with a dot shorthand and print wait.
  3. Which of these compile, and why? (a) Color a = .red; (b) final b = .red; (c) print(.red);

Application

  1. Write void paint(Color c) that prints Painting blue for .blue, and call it with a shorthand.
  2. Using the Size enum { small, medium, large }, write a switch expression that gives 250, 350, 450, and print the price of .medium.
  3. Fix this line so it compiles: if (.small == cup) print('small');
  4. Give a class Temperature a field double celsius and a named constructor Temperature.fromFahrenheit(double f). Create .fromFahrenheit(212) with a shorthand and print celsius.
  5. Write void brew({Size size = .medium}) that prints Brewing medium, then call it with no argument and with .large.

Challenge

  1. Store List<Size> cups = [.small, .large, .large]; and print the total price using the prices from question 5.
  2. Explain why String n = .small.name; fails, and give two ways to fix it.

Practice Answers

Question 1 — Solution

dart
enum Color { red, green, blue }

void main() {
  Color c = .green;
  print(c);
}
output
Color.green

Question 2 — Solution

dart
void main() {
  Duration wait = .new(seconds: 30);
  print(wait);
}
output
0:00:30.000000

Question 3 — Solution

Only (a) compiles. (b) final with no type gives no context: "No type was provided to find the dot shorthand 'red'." (c) print expects Object?, so Dart searches Object? for red and does not find it.

Question 4 — Solution

dart
enum Color { red, green, blue }

void paint(Color c) => print('Painting ${c.name}');

void main() {
  paint(.blue);
}
output
Painting blue

Question 5 — Solution

dart
enum Size { small, medium, large }

int price(Size size) => switch (size) {
  .small => 250,
  .medium => 350,
  .large => 450,
};

void main() {
  print(price(.medium));
}
output
350

Question 6 — Solution

dart
enum Size { small, medium, large }

void main() {
  Size cup = .small;
  if (cup == .small) print('small');
}
output
small

Question 7 — Solution

dart
class Temperature {
  final double celsius;
  Temperature.fromFahrenheit(double f) : celsius = (f - 32) * 5 / 9;
}

void main() {
  Temperature boiling = .fromFahrenheit(212);
  print(boiling.celsius);
}
output
100.0

Question 8 — Solution

dart
enum Size { small, medium, large }

void brew({Size size = .medium}) => print('Brewing ${size.name}');

void main() {
  brew();
  brew(size: .large);
}
output
Brewing medium
Brewing large

Question 9 — Solution

dart
enum Size { small, medium, large }

int price(Size size) => switch (size) {
  .small => 250,
  .medium => 350,
  .large => 450,
};

void main() {
  List<Size> cups = [.small, .large, .large];
  int total = 0;
  for (Size cup in cups) {
    total += price(cup);
  }
  print('Total: $total');
}
output
Total: 1150

Question 10 — Solution

The context type is String, so Dart looks for a static member small in String. Fix it by naming the enum, or by giving the shorthand its own Size context first:

dart
enum Size { small, medium, large }

void main() {
  String n = Size.small.name;
  Size cup = .small;
  print(n);
  print(cup.name);
}
output
small
small

Quick Revision

  1. What is a context type?
  2. What does .small mean when the context type is Size?
  3. Which side of == gives the context?
  4. How do you call an unnamed constructor with a shorthand?
  5. Why does var cup = .small; fail?
  6. Which Dart version introduced dot shorthands?
  7. When should you spell out the type instead?

Key Takeaways

  • A dot shorthand like .small lets Dart fill in the type name from the context type.
  • Typed variables, parameters, defaults, return types, list types, and switches all give a context.
  • In == and !=, the left side gives the context: cup == .small, never .small == cup.
  • Static members (.zero, .parse) and constructors (.new(), .origin(), .fromJson()) work too.
  • var, final without a type, Object?, and dynamic give no useful context.
  • The lookup always happens in the context type, so a chain must end in that type.
  • Dot shorthands need language version 3.10 or later.
  • Use them where the type is obvious nearby; spell it out where it is not.

What's Next?

Lecture 130 — Class Modifiers. Dot shorthands make using a type shorter, but they say nothing about how others may use your classes. Can another library extend your class, implement it, or only construct it? Next we will meet base, interface, final, and the other class modifiers that answer those questions.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.