头图

Interoperability enhancements, platform-specific networking components, optimized type inference, and recent updates to the null-safe language milestone

Posted by Michael Thomsen, Product Manager, Flutter & Dart, Google

The Dart 2.18 stable release was also released alongside the Flutter 3.3 stable release. This update brings a preview of Dart's Objective-C \& Swift interoperability feature, as well as a package of iOS/macOS web components built on this feature. The new Dart also includes type inference optimizations for generic methods, performance improvements for asynchronous code, new functionality in pub.dev, and some tweaks to our tools and core libraries.

At the end of the article, we also give the latest data on the state of the null-safe migration, and an important update on the roadmap for the eventual full implementation of Dart's null-safe features. Be sure to read to the end.

Dart intermodulates with Objective-C and Swift

Back in 2020, we released a preview of the Foreign Functional Interface (FFI) for calling native C language interfaces, and we will release it in Dart 2.12 in 2021. Since then, a large number of packages have integrated with the existing native C language interface API by taking advantage of FFI, such as file_picker , printing , win32objectboxrealmisartflite_flutter dbus package。

The Dart team hopes that interoperability between mainstream programming languages can be supported on all platforms that Dart can run on. The official version 2.18 has reached the next milestone of this goal. Now, Dart code can directly call Objective-C and Swift code, Mainly used to call APIs on macOS and iOS platforms. Dart supports "full call" - from the command line code on the backend to the Flutter interface on the frontend, you can use this interoperability mechanism in any application.

This new mechanism stems from the fact that Objective-C and Swift code can be called from C code through the API binding mechanism. Dart's ffigen tool can create these bindings through API header files. Let's look at an example.

Example of Manipulating Time Zones in Objective-C

There is an API for querying time zone information on macOS, which can be called through the NSTimeZone class. Developers can use this API to query the time zone and UTC time zone offset set by the user for the device.

The following sample Objective-C application calls this time zone API to get the system time zone setting and GMT offset.

 #import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSTimeZone *timezone = [NSTimeZone systemTimeZone]; // Get current time zone.
        NSLog(@"Timezone name: %@", timezone.name);
        NSLog(@"Timezone offset GMT: %ld hours", timezone.secondsFromGMT/60/60);
    }
    return 0;
}

The sample application first imports the Foundation.h header file, which contains the API headers for Apple's base library. In the following method body, it calls the systemTimeZone method of NSTimeZone, which returns an instantiated NSTimeZone and contains the time zone information set by the device.

Finally, the application prints two lines to the console, containing the timezone name and the hourly offset from UTC:

 Timezone name: Europe/Copenhagen
Timezone offset GMT: 2 hours

Example of manipulating timezones in Dart

Let's repeat the implementation just now with Dart and Objective-C interop.

First create an application via the Dart command line:

$ dart create timezones

Next, add the configuration parameters of ffigen 4998472e1677b0d9514aea95e4de7836--- to your pubspec file. These configurations will set the header file path in the headers and list the wrapper classes to be generated. Objective-C interface:

 ffigen:
  name: TimeZoneLibrary
  language: objc
  output: "foundation_bindings.dart"
  exclude-all-by-default: true
  objc-interfaces:
    include:
      - "NSTimeZone"
  headers:
    entry-points:
      - "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/
         Headers/NSTimeZone.h"

This configures Objective-C bindings for the NSTimeZone.h header file, and only includes the APIs in the NSTimeZone interface, then runs the following code to generate the wrapper class:

$ dart run ffigen

This command will create a new dart file containing various API bindings foundation_bindings.dart , after calling this file, we can write the main Dart method ( main ), this method "Mirror" the Objective-C code, as follows:

 void main(List<String> args) async {
  const dylibPath =
      '/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation';
  final lib = TimeZoneLibrary(DynamicLibrary.open(dylibPath));

  final timeZone = NSTimeZone.getLocalTimeZone(lib);
  if (timeZone != null) {
    print('Timezone name: ${timeZone.name}');
    print('Offset from GMT: ${timeZone.secondsFromGMT / 60 / 60} hours');
  }
}

That's it, this new feature is available as experimental support since Dart 2.18. It enhances Dart's basic interoperability features and can call macOS and iOS APIs directly in Dart code or through Flutter plugins.

We very much welcome feedback from developers, and you can give us feedback via our GitHub Issue, let us know what is working well, what needs to be improved, and any issues you encounter. For more information on interoperability, see the Dart documentation: Interoperating with Objective-C and Swift using package:ffigen .

Platform specific http library

Dart comes with a common library http which can be applied to multiple platforms. Using this library for network requests can avoid considering the different situations of each platform. But sometimes, developers may want to build on a platform using the platform's network request API.

For example, Apple's network request library NSURLSession can restrict access to Wi-Fi only or require a VPN to connect. To support these use cases, we created a new network request package: cupertino_http , which is based on the new Objective-C interop mentioned in the previous section, and from the network request library in the Apple Foundation library "Extract" a large number of APIs.

cupertino_http Example

In this example, the HTTP client for the Flutter app uses the cupertino_http on macOS and iOS, and the normal dart:io library on other platforms:

 late Client client;
if (Platform.isIOS || Platform.isMacOS) {
  final config = URLSessionConfiguration.ephemeralSessionConfiguration()
    ..allowsCellularAccess = false
    ..allowsExpensiveNetworkAccess = false;
  client = CupertinoClient.fromSessionConfiguration(config);
} else {
  client = Client(); // 使用基于 dart:io 的 HTTP 客户端
}

After initial configuration like this, the application will perform specific network requests on different platforms, such as the current get() request similar to the following:

 final response = await get(
  Uri.https(
    'www.googleapis.com',
    '/books/v1/volumes',
    {'q': 'HTTP', 'maxResults': '40', 'printType': 'books'},
  ),
);

When the common interface cannot be used, you can call Apple's network request API through cupertino_http :

 final session = URLSession.sessionWithConfiguration(
    URLSessionConfiguration.backgroundSession('com.example.bgdownload'),
    onFinishedDownloading: (s, t, fileUri) {
      actualContent = File.fromUri(fileUri).readAsStringSync();
    });

final task = session.downloadTaskWithRequest(
    URLRequest.fromUrl(Uri.https(...))
    ..resume();

Use platform-specific networking in multi-platform applications

Our design goal is still to maintain the multi-platform versatility of the application as much as possible, so we reserve the basic network requests and other operations common to multiple platforms for the http API, and can configure the network on different platforms through configuration files. request library. Developers can use the package:http Client API to reduce writing platform-specific code, which can be configured per platform and used in a platform-independent manner.

Dart 2.18 provides experimental support for the platform-specific http library for the package:http Client API :

  • Using NSURLSession based on macOS/iOS
  • Using Cronet on Android is based on Cronet , a very popular network request library on Android

Combining a common Client API with several different network request implementations gives you the benefit of both using platform-specific behavior while still maintaining the same set of shared network request resources. We'd love to hear your feedback on GitHub .

Enhanced type inference

Dart uses many common methods, consider this fold method that converts a collection element to a single value. Here is an example of summing the numbers in a collection:

 List<int> numbers = [1, 2, 3];
final sum = numbers.fold(0, (x, y) => x + y);
print(‘The sum of $numbers is $sum’);

Before Dart 2.17 this method would return a type error:

 line 2 • The operator ‘+’ can’t be unconditionally invoked because the receiver can be ‘null’.

Dart cannot combine information between multiple parameters for type inference. This resulted in x type being indeterminate. To correct this potential error, you need to specify the type:

final sum = numbers.fold(0, (int x, int y) => x + y)

Dart 2.18 enhances type inference. In the previous example, Dart would perform static analysis and deduce that both x and y are non-null integers. This change allows you to write more concise Dart code while retaining the robustness of strong type inference.

Asynchronous function performance enhancement

This version of Dart optimizes the way the Dart VM executes async and async* / sync* . This reduces code size: on two of Google's large applications, we saw about a 10% reduction in AOT snapshot product size. The performance improvement is also reflected in our microbenchmarks.

There are some additional minor behavioral changes included with the VM, see the release notes for more.

Improvements to the Pub.dev website

Combined with the changes released in version 2.18, we have also brought two new changes to the package ecological website pub.dev .

Typically, individual package developers maintain and release new packages in their spare time, which can consume a lot of their time and resources. In order to facilitate other users to sponsor, we support a brand new funding tag in pubspec 9732681576d92d60f90bae44076e42c2---, which package developers can use to list one or more links to sponsor their continuous development, These links will appear in the pub.dev sidebar of the website.

To learn more visit the pubspec documentation .

In addition, we also want to promote a rich ecosystem of open source packages, and to highlight this, the automatic scoring system at pub.dev will award an additional 10 points for packages using an OSI-approved license .

some breaking changes

Dart has a strong focus on simplicity and learnability, so we've been careful when adding new features. One way to keep it simple is to remove old features and APIs that are rarely used or have better replacements. Dart 2.18 cleans up this type of entry and includes a few breaking changes:

  • We added unified dart CLI developer tools in October 2020. In 2.18, we completed this transition. This release removes the last two deprecated command line tools: dart2js (replaced to use dart compile js ) and dartanalyzer (replaced to use dart analyze ).
  • With the introduction of language versioning, the pub command will generate a new parsing file: .dart_tool/package_config.json (previously used .packages format files cannot contain versions), Now that we have stopped using the .packages files, if you have any .packages files, you can delete them.
  • Classes that do not inherit from Object can no longer be used as mixins (breaking change #48167 ), this behavior was never intentionally encouraged.
  • dart:io in RedirectException uri property changed to nullable (breaking change #49045 ).
  • dart:io Constant following the SCREAMING_SNAKE convention in the web request API has been removed (breaking change #34218 ), use the corresponding lowerCamelCase constant instead.
  • Dart VM no longer restores initial terminal settings on exit, programs that change standard input settings lineMode and echoMode are now responsible for restoring settings when the program exits (breaking change #45630 ).

empty security update

Since airsafe was released in beta in November 2020 and officially launched with Dart 2.12 in March 2021, we're happy to see airsafe widely used.

First, developers of most of the popular packages on pub.dev have migrated to null-safe. Our analysis shows that the top 250 most used packages already support null safety, and 98% of the top 1,000 packages already support null safety.

Second, most developers are already developing in a codebase with complete null safety. This is critical because Dart's sane null-safety doesn't come into play until all code and all dependencies (including transitive) are migrated, which we're tracking via telemetry from the flutter run command at this point.

The figure below shows a comparison of flutter run command execution in Unsound and Sound null safety. Both were zero before null safety was introduced. Then the non-sound null security grew rapidly. At this time, the application began to gradually migrate to null security. The developers first migrated some parts, but some parts still need to be migrated. Over time, we can see a steady increase in the sound air safety curve, and by the end of last month, sound air safety had four times more executions compared to non-sound air safety. We hope that over the next few quarters we will see sound air safety reach 100%!

Important Air Safety Roadmap Update

Supporting both non-sane and sane null safety inevitably adds overhead and complexity.

First, Dart developers need to learn and understand both patterns. Whenever you read a piece of Dart code, you need to check the language version .

Second, supporting both modes at the compiler and runtime also slows down the development of the Dart SDK to support new features.

Based on the overhead of non-sound null-safety and the very impressive stats mentioned in the previous section, our goal is to transition to supporting only sane null-safe and stop supporting both non-null-safe and non-sane null-safe modes, we temporarily It is scheduled for release in mid-2023.

This will mean the end of support for Dart 2.11 and earlier. pubspec with SDK constraints and lower bound less than 2.12 will no longer be resolved in Dart 3 and later. In source code containing language tags, it will fail if set to less than 2.12 (eg //@dart=2.9 ).

If you've migrated to sane null-safety, your code will work with full null-safety in Dart 3. If you haven't already, our advice is to start migrating now. Learn more about these changes in this issue .

Summarize

Interop with the likes of Objective-C and Swift, the network request library, type inference for the Dart programming language, and updates to pub.dev are officially available. To start the experience, please download the latest Dart 2.18 official version, or experience it directly in Flutter 3.3, or directly experience the Dart programming language in DartPad.

Finally, the migration of null safety, please start the migration now, and work with us to build and experience the Dart programming language with sound null safety features!

Original link :

https://medium.com/dartlang/dart-2-18-f4b3101f146c

Localization : CFUG Team: \@chenglu, \@Vadaski, \@MeandNi, \@Realank

Chinese link :

https://flutter.cn/posts/dart-2-18


Flutter
350 声望2.5k 粉丝

Flutter 为应用开发带来了革新: 只要一套代码库,即可构建、测试和发布适用于移动、Web、桌面和嵌入式平台的精美应用。Flutter 中文开发者网站 flutter.cn