By Kevin Moore & Michael Thomsen

Dart version 2.13 is now available with the addition of type aliasing, currently the second most popular language feature. Dart 2.13 also offers improved Dart FFI and better performance, and we have a new official mirror for Dart. This article brings you up to date on the air safety features introduced in version 2.12, introduces new features in version 2.13, and updates on Docker and Google Cloud support for the Dart backend. Additional changes will also be announced in future releases.

Air security update

With Dart 2.12, released in March, we introduced robust air security features. Space safety is an important feature that Dart recently introduced to help you avoid null-value errors, which are often difficult to detect, and improve your productivity. We hope that the developers who publish the package will follow up with this release and update the package shared on pub.dev to support air safety.

We are extremely pleased to see that in just a few months since launch, air security has been widely adopted, with 93% of the top 500 most popular packages in Pub.dev now supporting air security. I would like to extend my sincere thanks to all the package developers who have been so quick to follow up and help keep the entire ecosystem moving forward!

With so much of the Package supporting empty security, you can start thinking about moving your application to an environment that uses empty security. To start the migration, first check your application’s dependencies using dart Pub Outdated. For details, see the Null Safe Migration guide. We have also tweaked the Dart Create and Flutter Create templates so that they are now null safe by default in new applications and packages.

Roll out type aliasing

Type aliasing, a new language feature in version 2.13 and one that developers have been waiting for, was the second most reported language problem. With this capability, developers can create aliases for function types, but not for any other types.

With type aliases you can create a new name for any existing type, and then use the newly created name wherever the original type can appear. Creating a new name doesn’t really define a new type, it just introduces a short alias. The alias can even pass the type-equivalence test:

typedef Integer = int;

void main() {
  print(int == Integer); // true
}
Copy the code

So, how can a type alias be used? A common use is to give a type a shorter or more descriptive name to make your code easier to understand and maintain.

For example, aliasing JSON types is a good use (this example was provided by GitHub user Levi-Lesches, thanks). In the following example, we can define a new type alias Json that describes a Json document as a map with a String key and an arbitrary value (using dynamic typing). This way, we can use the JSON type alias when we define a constructor named fromJson and a JSON GET function.

typedef Json = Map<String.dynamic>;

class User {
  final String name;
  final int age;

  User.fromJson(Json json) : 
    name = json['name'],
    age = json['age'];

  Json get json => {
    'name': name, 
    'age': age,
  };
}
Copy the code

You can also call the constructor on a type alias that refers to a class, as in the following example:

main() {
  var j = Json();
  j['name'] = 'Michael';
}
Copy the code

By using type aliases to refer to complex types, you make it easier for readers to understand the invariants of your code. For example, the following code defines a type alias to describe a map with a key value of generic type X and a value of type List

. If you give the type a name with a single type parameter, the general structure of the map becomes clearer to the code reader.

typedef MapToList<X> = Map<X, List<X>>;
void main() {
  MapToList<int> m = {};
  m[7] = [7]; // OK
  m[8] = [2.2.2]; // OK
  for (var x in m.keys) {
    print('$x --> ${m[x]}');
  }
}

=>

7 --> [7]
8 --> [2.2.2]
Copy the code

If you try to use a mismatched type, you will get an analysis error:

m[42] = ['The'.'meaning'.'of'.'life']; 


=>

The element type 'String' can't be assigned to the list type 'int'.
Copy the code

You can even use type aliases to rename classes in common libraries. Suppose you now have a PoorlyNamedClass class in the public library that you want to rename to BetterNamedClass. If you simply rename the class, you will get a burst compilation error on your API client’s side. With type aliases, this is not a problem, and you can rename anything you want, just define a new type alias for the old class name and add a few lines of @deprecated annotations to the old name. That way, code using PoorlyNamedClass gets a warning but continues to compile and run as usual, giving the user time to upgrade their code.

mylibrary.dart:

class BetterNamedClass {}

@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;

main.dart


import 'mylibrary.dart';


voidmain() { PoorlyNamedClass p; } = >'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
Copy the code

Here’s how to implement BetterNamedClass and deactivate PoorlyNamedClass (in a file called myLibrary.dart).

class BetterNamedClass {... }@Deprecated('Use BetterNamedClass instead')
typedef PoorlyNamedClass = BetterNamedClass;
Copy the code

Here’s what happens when you try to use the PoorlyNamedClass:

import 'mylibrary.dart';
voidmain() { PoorlyNamedClass p; } = >'PoorlyNamedClass' is deprecated and shouldn't be used. Use BetterNamedClass instead.
Copy the code

The type aliasing feature is available as early as Dart version 2.13. To enable this feature, set the Dart SDK constraint of the lower version in your PubSpec to the lowest version 2.13, as shown below:

Environment: the SDK: "> = 2.13.0 < 3.0.0"Copy the code

This feature supports backward compatibility thanks to the versioning of the language. That is, packages with SDK constraints lower than 2.13 can safely refer to type aliases defined in package 2.13, although packages prior to 2.13 cannot define their own type aliases.

Dart 2.13 FFI changes

We’ve also introduced some new functionality in Dart FFI, which is the interoperability mechanism for calling C code.

First, FFI now supports structures containing inline arrays (#35763). Suppose a C structure has the following inline array:

struct MyStruct {
  uint8_t arr[8];
}
Copy the code

Now, simply assign an element type containing a type argument to Array to wrap the structure directly in Dart, as shown below:

class StructInlineArray extends Struct {
  @Array(8)
  external Array<Uint8> arr;
}
Copy the code

Second, FFI now supports encapsulated structures (#38158). Structures are typically placed in memory so that their members within address boundaries can be accessed more easily by the CPU. When using a wrapper structure, some padding bytes are often omitted in a platform-specific way to reduce the overall memory footprint. With the new @packed (

) annotation, you can easily specify fill bytes. For example, the following code creates a structure that specifies that its byte alignment is 4 when in memory.

@Packed(4)
class TASKDIALOGCONFIG extends Struct {
  @Uint(32)external int cbSize;
  @IntPtr(a)external int hwndParent;
  @IntPtr(a)external int hInstance;
  @Uint(32)external intdwFlags; ... }Copy the code

Performance improvements in Dart 2.13

We are constantly working to reduce the application size and memory footprint of the Dart code. In large Flutter applications, the internal structure of the aoT-compiled Dart program metadata can be a significant memory footprint. Much of this metadata exists to enable functions such as hot overloading, interactive debugging, and formatting readable stack traces that are never used in the applications that need to be deployed. Over the past few years, we’ve been refactoring the Dart native runtime environment to eliminate as much of this overhead as possible. Some of these improvements apply to all Flutter applications built in version mode, while others require the –split-debug-info flag to split out debugging information in AOt-compiled applications, thereby abandoning readable stack traces.

Dart 2.13 is a big improvement in memory consumption, with a significant reduction in the amount of space taken up by program metadata when using –split-debug-info. For example, the space footprint of Flutter Gallery is reduced by 30%: in –split-debug-info mode, application metadata takes up 5.7MB in Dart 2.12, but only 3.7MB in Dart 2.13. Take the Flutter Gallery application for example. On Android, the release APK of Flutter Gallery containing debug information is 112.4MB, or 106.7MB without it (a 5% reduction in total volume). A number of resources are included in this APK. In terms of metadata volume within APK alone, it went from 5.7MB on Dart 2.12 to 3.7MB on Dart 2.13 (a 35% reduction!). .

If the application size and memory footprint are important to you, use the –split-debug-info flag to omit debugging information. Note that once you have done this, you need to use the interest_command to re-make the stack trace readable.

Dart official Docker image release and Cloud support

Dart is now available in official images, and while Dart has long provided Docker images, these new Dart images are tested and verified by Docker in order to follow best practices. They also support AOT compilation, which can greatly reduce the size of build containers, and can speed deployment in container environments such as Cloud Run.

While Dart has always focused on making application frameworks like Flutter build great interfaces on every screen, we realize that most user experiences have at least one hosting service behind them. By making it easy for Dart to build back-end services to support a full-stack experience, developers can scale their applications to the cloud using the same language and business logic as front-end widgets.

Dart is typically used on the back end of a Flutter application, particularly in line with the simplicity and scalability of Google’s serverless management platform Cloud Run. This also includes zero scaling, meaning there is no cost when your back end does not handle any requests. We partnered with the Google Cloud team to provide the Functional framework for Dart, a collection of packages, tools, and instances that enables developers to easily write Dart functions in lieu of full server deployments that handle HTTP requests and CloudEvents.

You can check out our official Google Cloud documentation to get started.

Follow-up Update notice

There will be some exciting changes in the next version. As always, you can follow our work with the Language Funnel tracker.

One area we’ve been working on improving is defining a new set of Canonical Lint for Dart and Flutter. Lint is an efficient way to configure Dart static analysis, but since there may be hundreds or thousands of Lint enabled or disabled, it can sometimes be difficult to decide. Currently, we are going to define two sets of Canonical Lint to be used by default in the Dart and Flutter projects. Both sets of Lint are expected to be enabled by default in the next stable release. If you want a preview, check out the linTS and Flutter_LINTS packages.

Finally, if you deeply nested the Dart VM runtime environment, be aware that we intend to deprecate its existing mechanisms. We will replace it with a faster, more flexible model based on THE Dart FFI (see Tracking issue #45451).

Dart version 2.13 is now available

Dart version 2.13, now available with Dart 2.13 and Flutter 2.2 SDK, adds type aliasing and improves FFI.

If you’ve been waiting for the right time to migrate your dependencies to an empty security environment, check again using Dart Pub Outdated. With 93% of the top 500 most popular packages now migrated, this might be a good time for you to do so. Thanks to those developers who have migrated!

Welcome to try out the new and improved features introduced in this guide, and tell us what you think after using them. Let us know in the comments section below.