This article examines some of the grammatical sugar implementations of Dart in the Flutter. What does Dart do behind the keyword that you clearly stated.

As shown in the figure below, the cause was a very basic question I saw in the group yesterday, asking: “Why does this code not nullable the user?” .

The question is simple:

  • 1. Dart Sound Null Safety declares that non-null objects do not need to be Null; (Judge if you want, there will be a warning ⚠️)
  • 2, uselateIf an object declared by a keyword is accessed without being initialized, an error will be reported.

So it’s a very simple question, you just say User, right? If you add late to an object that should not be empty, why not initialize it immediately?

late

First, as shown in the figure below, we write a simple code that declares a playerAnimation object through the late, and then after running the code, we extract the compiled app.dill through dump_kernel.dart.

As shown in the figure below, by extracting the compiled code, you can see that the playerAnimation is actually converted to an Animation? Get playerAnimation() when playerAnimation is called. If playerAnimation == null, then LateError is thrown.

So when we visitedlateThe declared object is, and if the object is not initialized, an exception is returned.

typedef

After introducing late, we’ll introduce typedef. Typedef can be used for new type alias functions starting in Dart 2.13, such as:

// Type alias for functions (existing)
typedef ValueChanged<T> = void Function(T value);

// Type alias for classes (new!)
typedef StringList = List<String>;

// Rename classes in a non-breaking way (new!)
@Deprecated("Use NewClassName instead")
typedef OldClassName<T> = NewClassName<T>;

Copy the code

So how does a typedef work? As you can see in the figure below, the _getDeviceInfo method is replaced by the List

method after compilation, so the StringList does not actually participate in the compilation of the code, so it does not affect the efficiency of the code.

For example, as shown in the figure below, you can see that SelectItemChanged declared as final field (Dynamic) →? void selectItemChanged; .

Then we look at another phenomenon with Dart’s tear-off, as shown in the figure below. You can see that we have extracted the toString method from an arbitrary object x, and with the closure, we can call x as if it were a regular instance.

If you call a function on an object and omit the parentheses, Dart calls it “tear-off” : a closure that takes the same arguments as the function, and executes its functions when called, such as names.foreach (print); Equivalent names. ForEach ((name) {print (name); });

So what does the compiled getToString method look like?

As shown in the figure below, you can see that getToString is compiled as a static method, and ToStringFn is not actually running, but is replaced with the corresponding ()-> core:String.

So for compiled code,typedefThere is no impact on performance or run results.

extension

Dart makes it easy to extend objects with an extension. How does the extension keyword extend objects over the original objects?

As shown in the figure below, we declare an enumeration of Cat and extend Cat to assign a value to each value of the enumeration and add the talk method.

As shown in the figure below, the enumerated values in Cat are compiled to a static final fixed address, and the talk and value values in CatExtension are pointed to the new location.

The name of CatExtension and the name of talk change to the static method in the file where the talk method is located. The talk method is defined first and then called through the get implementation of tearOFF. Basically all methods defined in an Extension will have method and tearOFF.

As shown in the figure below, in the use of the Cat, the compiled can see the talk () is actually carried out the main: : CatExtension | talk.

async / await

Finally, async/await. We all know that this is the Future’s syntax-sugar in Dart. How does this syntax-sugar work after compilation?

As you can see, the loadmore method adds a lot of code after compilation. It defines a _Future

async_Future and returns at the end. The code we need to execute is wrapped in async_op to execute. Async_op performs a try catch operation on the executed content and returns with _completeOnAsyncError.

This is why we can’t catch an exception when we try a Future externally, so we need to catch an exception with.onError((Error, stackTrace) => NULL) as shown in the figure below.

Understanding the implementation behind these keywords will help you organize your code more gracefully during the daily development of Flutter, thus avoiding many unnecessary problems.

Of course, if you don’t use it, it’s good to use it for interviews, isn’t it?