Dart 2.17: Productivity and integration

Language features. Productivity tools. Deeper & broader platform integration.

Michael Thomsen
8 min readMay 11, 2022


Today at Google I/O, we announced a new Dart SDK, version 2.17. This release builds on our core themes of leading productivity and platform portability. It offers new language features: enums with member support, improved parameter forwarding to super classes, and more flexibility for named parameters. We have improved tools with a new major version of package:lints — our tool support for checking Dart code against our best practices — and a broad update of core library API documentation with rich code samples. For improved platform integration we have new templates for using dart:ffi (native C interop) in Flutter plugins, experimental support for RISC-V processors, and support for signing macOS and Windows executables.

Dart 2.17 reease banner

New language features for improved productivity

We constantly evolve the Dart language to make you more productive — both by adding new features and by making improvements to existing features. Dart 2.17 adds major new support for members on enums, refines how you can use named arguments in constructors, and makes code for forwarding parameters to superclasses much less verbose and repetitive.

Enhanced enums with members

Enums are great for representing a discrete set of states. For example, we might model water as enum Water { frozen, lukewarm, boiling }. But what if we want to add some methods on the enum — for example, to convert each of the states to a temperature, and support converting the enum to a String? We could perhaps use extension methods to add a waterToTemp() method, but we’d have to be careful to keep that in sync with the enum. For the String conversion we’d prefer to override toString(), but that hasn’t been supported.

With Dart 2.17 we now have general support for members on enums. That means we can add fields holding state, constructors that set that state, methods with functionality, and even override existing members. Many of you have been requesting this capability; it was our third highest voted issue in the language tracker.

For our water example, we can add an int field holding the temperature and a default constructor that takes an int:

enum Water {

final int tempInFahrenheit;
const Water(this.tempInFahrenheit);

To ensure the constructor is called when the enum is created, we need to invoke it for each enum value:

enum Water {


To support the conversion to String, we simply override toString, which enums inherit from Object:

String toString() => "The $name water is $tempInFahrenheit F.";

And with that, you have a complete enum that can be easily instantiated and you can invoke methods on:

void main() {
print(Water.frozen); // Prints “The frozen water is 32 F.”

A complete example of these two approaches is illustrated below; we find the new Dart 2.17 version much easier to read and maintain.

Super initializers

When you have a class inheritance hierarchy, a common pattern is to pass some constructor parameters to the constructor of the superclass. To do that, the sub-class needs to 1) list each parameter in its own constructor, and 2) invoke the super constructor with those parameters. That leads to boilerplate code: lots of repetition, making the code harder to read and more bothersome to maintain.

Several Dart community members helped make this happen. GitHub user @roy-sianez filed a language issue regarding this about half a year ago; suggesting something similar to what GitHub user @apps-transround had suggested earlier: that we could perhaps resolve this by introducing a new construct to express that a parameter was specified in the super class. We thought that was a great idea, so this has been added to Dart 2.17. As you can see from the following example, this is especially relevant for Flutter widget code. In fact, when we applied the new feature to the Flutter framework, we saw a total reduction of nearly two thousand lines of code!

Named args everywhere

Lastly, we’ve refined how named arguments work when you invoke a method. Previously, these had to appear last in the list of arguments to the method. This was annoying in cases where you’d prefer to place a positional argument last to make the code more readable. For example, see the following invocation of the List<T>.generate constructor — before, the growable argument had to be placed last, making it easy to miss below the large positional argument containing the generator itself. Now you can order them as you prefer, allowing you to place the small named arguments first and the generator last.

For further examples of these three features in actions, see our updated samples for enums, super initializers, and named parameters.

Productivity tools

Continuing the productivity theme, we have several improvements in our core tools.

In Dart 2.14 we introduced package:lints, which works with the Dart analyzer to help you write Dart code that prevents errors and uses a canonical style enabling more effective code reviews. Since then a number of new lints have been made available in the analyzer, which we’ve carefully triaged and from that selected ten new lints for all Dart code, and two new lints specifically for Flutter code. These include lints for ensuring your imports are included in your pubspec file, preventing the misuse of null checks on type parameters, and ensuring a consistent style for child properties. You can upgrade to the new lints with a simple command:

  • For Dart packages:
    dart pub upgrade —-major-versions lints
  • For Flutter packages:
    flutter pub upgrade —-major-versions flutter_lints

SecureSockets are commonly used to enable TCP sockets secured with TLS and SSL. Before Dart 2.17, debugging these sockets during development was tricky, as there was no way to inspect the secure data traffic. We’ve now added support for specifying a keyLog file. When specified, a line of text in the NSS Key Log Format is appended to the file when new TLS keys are exchanged with the server. This enables network traffic analyzer tools (such as Wireshark) to decrypt the content sent through the socket. For details, see the API docs for SecureSocket.connect().

API documentation generated by the dart doc tool is a critical asset to most Dart developers learning new APIs. While our core library APIs have long had rich textual descriptions, many developers have told us they prefer to learn APIs by reading sample code that uses the API. With Dart 2.17, we’ve completely overhauled all major core libraries, adding sample code to the top 200 most viewed pages, so they now have comprehensive sample code. As an example, compare the documentation for dart:convert in Dart 2.16 with the updated page for Dart 2.17; hopefully this makes the documentation much easier to consume.

Productivity gains come not only when we add new features to our platform, but also when we clean up our stack and remove features that are no longer used. This helps keep our surface area small, which is critical especially to new developers. Towards this, we’ve removed 231 lines of deprecated code from the dart:io library — if you are still using these deprecated APIs, you can upgrade to their replacements with dart fix. We’ve also continued our effort to remove deprecated Dart CLI tools, this time removing the dartdoc tool (use dart doc instead), and pub tool (use dart pub or flutter pub).

Broadening our platform integration and support

A second core theme is platform integration and support. Dart is a true multi-platform language. While we already support a wide range of platforms, we’re constantly evolving to ensure that you can deeply integrate with each supported platform, and also to support emerging platforms.

Dart FFI — our core mechanism for interop with C/native code — is a popular way of integrating Dart code with existing native platform code. On Flutter, this can be a great way to build plugins that use native APIs from the host platform (such as Windows win32 APIs). In Dart 2.17 and Flutter 3, we’ve added templates to the flutter tool so you can now easily create FFI Plugins that have a Dart API backed by dart:ffi calls into native code. For details, see the updated developing packages and plugins page on flutter.dev.

To enable use of FFI on platforms that have types specific to their ABI (application binary interface), FFI now supports ABI-specific types. For example, you can now use Long (long in C) to correctly represent a long integer with an ABI-specific size, which might be 32-bit or 64-bit depending on the CPU architecture. For the full list of supported types, see the list of “Implementers’’ in the AbiSpecificInteger API page.

When deeply integrating with native platforms using Dart FFI, you sometimes need to align the cleanup of memory or other resources (ports, files, and so on) allocated by Dart and the native code. This has historically been very tricky, as Dart is a garbage collected language that automatically handles cleanup. Dart 2.17 solves this by introducing the concept of a Finalizer, which includes a Finalizable marker interface for “tagging” objects that shouldn’t be finalized or discarded too early, and a NativeFinalizer class that can be attached to a Dart object to provide a call-back run when the object is about to be garbage collected. Together these allow for running cleanup code in both native and Dart code. For details, see the description and example in the API documentation for NativeFinalizer, or the documentation for WeakReferences and Finalizer for similar support in regular Dart code.

Our support for compiling Dart to native code is a core enabler of making Flutter apps have great startup performance and fast rendering. A second use case is the ability to compile Dart to executables with dart compile. These executables can run standalone on any machine without needing the Dart SDK to be installed. Another new capability in Dart 2.17 is support for signing of executables, which enables deployment on Windows and macOS where signing is often required.

We’re also continuing to broaden our set of supported platforms by staying at the forefront of new emerging platforms. RISC-V is a new innovative instruction set for processors. RISC-V International, a global non-profit organization, owns the RISC-V specification, making the instruction set free and open. This is still an emerging platform, but we’re excited about its potential, so our 2.17.0–266.1.beta Linux release (or later from our beta channel) includes experimental support for it. We’d love to hear your feedback, so please file an issue or post about your experience!

Get started with Dart 2.17!

We hope that today’s Dart 2.17 release excites you, improves your productivity, and enables even more platform integration for your apps. To get started, you can directly download the Dart 2.17 release, or get it embedded as part of today’s Flutter 3 SDK release.

We also invite you to check out the new content we’ve made available for Google I/O!



Michael Thomsen
Editor for

Product Manager working on Dart and Flutter. Helping developers is my passion!