Skip to content
Blog

Dart Dot Shorthand Syntax: Cleaner Enum and Static Member Access (Dart 3.10+)

Dot shorthands let you omit the type prefix when accessing enum values and static members. Learn the syntax, real use cases in Flutter UI code, and how to migrate.

Published on • October 15, 2026

AI Assistant

Dart 3.10 (released November 12, 2025) introduced dot shorthands: an expression can now start with a leading dot (.foo) and the compiler resolves it against the context type instead of requiring you to spell out the class or enum name. It is a small syntax feature with an outsized effect on the noise level of Flutter UI code, where enum values and named constructors appear on nearly every line.

This is a language-versioned feature. It only takes effect when a package’s language version is 3.10 or higher, so simply bumping your SDK constraint turns it on.

The core idea: the context type does the work

A dot shorthand is an expression that begins with ., followed by an identifier, a .new() call, or a const creation. The compiler looks at what type the surrounding expression expects — the context type — and resolves the member against that type.

enum Status { none, running, stopped, paused }

Status currentStatus = .running;   // Instead of Status.running
int port = .parse('8080');         // Instead of int.parse('8080')
BigInt big = .zero;                // Instead of BigInt.zero

In the first line, the declared type of currentStatus tells the compiler the shorthand means Status.running. In the second, the context type int makes .parse resolve to int.parse. There is no runtime magic here — this is purely a compile-time shorthand that expands to the fully qualified expression.

Where it shines

Enums in switch expressions

The highest-value use case is enum-heavy code, especially switch expressions where the enum type is obvious from the subject:

enum LogLevel { debug, info, warning, error }

String colorCode(LogLevel level) => switch (level) {
  .debug => 'gray',
  .info => 'blue',
  .warning => 'orange',
  .error => 'red',
};

Every case drops a repeated type name that carried no information. Equality checks work the same way, as long as the shorthand is on the right-hand side:

if (myColor == .green) { /* ... */ }   // OK: myColor is a Color
if (myColor != .blue) { /* ... */ }    // OK

Constructors, including generics

Dot shorthands invoke named constructors, factory constructors, and constructors of generic classes, with type arguments inferred from the context:

class Point {
  const Point(this.x, this.y);
  const Point.origin() : x = 0, y = 0;
  factory Point.fromList(List<double> list) => Point(list[0], list[1]);
  final double x, y;
}

Point origin = .origin();               // Instead of Point.origin()
Point p = .fromList([1.0, 2.0]);        // Instead of Point.fromList(...)
List<int> intList = .filled(5, 0);      // Instead of List.filled(5, 0)

.new() for field initializers

The .new shorthand calls the unnamed constructor. Its biggest practical win is cleaning up the repetitive field initializers in a State class, where the declared type already says everything:

class _PageState extends State<Page> {
  late final AnimationController _animationController = .new(vsync: this);
  final ScrollController _scrollController = .new();
  final GlobalKey<ScaffoldMessengerState> scaffoldKey = .new();
  Map<String, Map<String, bool>> properties = .new();
  // ...
}

Compare that with the pre-3.10 version, where every line repeated the type name — including the awkward <String, Map<String, bool>>{} that .new() replaces cleanly.

Constant contexts

Dot shorthands work in const expressions when the resolved member is itself a compile-time constant:

const Status defaultStatus = .running;
const Point myOrigin = .origin();
const List<Point> keyPoints = [.origin(), .new(1.0, 1.0)];

Rules and gotchas

Dot shorthands lean entirely on a clear context type, which produces a handful of sharp edges worth knowing before you adopt them broadly.

Chaining is allowed, but the result must match the context. The compiler first resolves the shorthand using the context type; any chained operations must then return a value of that same type:

String lowerH = .fromCharCode(72).toLowerCase();
// .fromCharCode(72) resolves against String, then .toLowerCase() is called on it.

Equality is asymmetric. The shorthand must be on the right side of == or !=, because the left operand supplies the context type. .red == myColor does not compile. Nor does comparing against a complex expression: myColor == (cond ? .green : .blue) is an error, as is casting away the context first ((myColor as Object) == .green).

Expression statements can’t start with .. A bare statement like .log('hello') has no context type to resolve against, and the grammar forbids leading dots on expression statements to keep parsing unambiguous. Assign it, return it, or pass it as an argument instead.

Union-type contexts are only partially supported. For a nullable context type T?, you can access static members of T (but not of Null). For FutureOr<T> — mainly relevant to async function returns — you can access static members of T, but not of Future itself.

Tooling maturity arrived after the language feature. Dart 3.11’s analyzer changelog specifically lists fixes for dot shorthand support in IDEs, so if you hit completion or navigation oddities on day one of 3.10, updating the SDK is the fix.

When to use it — and when not to

Reach for dot shorthands when:

  • The context type is genuinely obvious: variable declarations with explicit types, switch expressions over a known enum, parameters typed at the call site.
  • You’re removing repeated boilerplate like field initializers in Flutter State classes.
  • You want const collection literals of your own value types to read shorter.

Keep the explicit type name when:

  • The context type would be far away or non-obvious to a reader — long method chains, argument positions with loose types, or code where inference is doing heavy lifting.
  • Readability suffers for a team unfamiliar with the feature. Colors.green is unambiguous to every Dart developer; .green requires the reader to resolve the context mentally. This is a style decision, not a correctness one.

Migration

There is no mechanical rewrite for adopting dot shorthands — the feature is opt-in syntax, not a breaking change. The migration path is:

  1. Set your package’s SDK constraint to sdk: ^3.10.0 (or higher) in pubspec.yaml, which bumps the language version and enables the feature.
  2. Update to a Flutter/Dart SDK of 3.10 or later; 3.11+ is recommended for solid IDE support.
  3. Adopt incrementally. dart format handles the syntax, and there is no flag-day migration — mixed explicit and shorthand style compiles fine.

Because the feature is gated on language version, a monorepo can migrate package by package without breaking anything that still targets 3.9.

Sources