Skip to content

Why Flutter Uses Dart

Adarsh Kumar Maurya edited this page Dec 8, 2018 · 1 revision

Many linguists believe that the natural language a person speaks affects how they think. Does the same concept apply to computer languages? Programmers working in different kinds of programming languages often come up with radically different solutions to problems. As a more extreme example, computer scientists eliminated the goto statement to encourage more structured programs (not quite the same as totalitarian leaders in the novel 1984 expunging heretical words from natural language to eliminate thoughtcrimes, but you get the idea).

What does this have to do with Flutter and Dart? Quite a bit actually. The early Flutter team evaluated more than a dozen languages, and picked Dart because it matched the way they were building user interfaces.

Here is a quick list of the Dart features that together make it indispensable for Flutter:

  • Dart is AOT (Ahead Of Time) compiled to fast, predictable, native code, which allows almost all of Flutter to be written in Dart. This not only makes Flutter fast, virtually everything (including all the widgets) can be customized.

  • Dart can also be JIT (Just In Time) compiled for exceptionally fast development cycles and game-changing workflow (including Flutter’s popular sub-second stateful hot reload). Dart makes it easier to create smooth animations and transitions that run at 60fps. Dart can do object allocation and garbage collection without locks. And like JavaScript, Dart avoids preemptive scheduling and shared memory (and thus locks). Because Flutter apps are compiled to native code, they do not require a slow bridge between realms (e.g., JavaScript to native). They also start up much faster.

  • Dart allows Flutter to avoid the need for a separate declarative layout language like JSX or XML, or separate visual interface builders, because Dart’s declarative, programmatic layout is easy to read and visualize. And with all the layout in one language and in one place, it is easy for Flutter to provide advanced tooling that makes layout a snap. Developers have found that Dart is particularly easy to learn because it has features that are familiar to users of both static and dynamic languages. Not all of these features are unique to Dart, but the combination of them hits a sweet spot that makes Dart uniquely powerful for implementing Flutter. So much so, it is hard to imagine Flutter being as powerful as it is without Dart.

The rest of this article goes into more depth about many of the characteristics of Dart (including its standard libraries) that make it the best language for implementing Flutter.

Compilation and execution

You can skip this section if you already know about topics like static versus dynamic languages, AOT and JIT compilation, and virtual machines.

Historically, computer languages have been divided into two groups: static languages (e.g., Fortran or C, where variables are statically typed at compile time), and dynamic languages (e.g., Smalltalk or JavaScript, where the type of a variable can change at run time). Static languages were typically compiled to produce native machine code (or assembly code) programs for the target machine, which at run time were executed directly by the hardware. Dynamic languages were executed by an interpreter, without producing machine language code.

Of course, things eventually became much more complicated. The concept of a virtual machine (VM) became popular, which is really just an advanced interpreter that mimics a hardware machine in software. A virtual machine makes it easier to port a language to new hardware platforms. In this case, the input language of a VM is often an intermediate language. For example, a programming language (such as Java) is compiled into a intermediate language (bytecode) and then executed on a VM (the JVM).

In addition, there are now just-in-time (JIT) compilers. A JIT compiler runs during execution of the program, compiling on the fly. The original compilers that execute during creation of the program (before runtime) are now called ahead-of-time (AOT) compilers.

In general, only static languages are amenable to AOT compilation into native machine code because machine languages typically need to know the type of data, and in dynamic languages the type is not fixed ahead of time. Consequently, dynamic languages are typically interpreted or JIT compiled.

When AOT compilation is done during development, it invariably results in much slower development cycles (the time between making a change to a program and being able to execute the program to see the result of the change). But AOT compilation results in programs that can execute more predictably and without pausing for analysis and compilation at runtime. AOT compiled programs also start executing faster (because they have already been compiled).

Conversely, JIT compilation provides much faster development cycles, but can result in slower or jerkier execution. In particular, JIT compilers have slower startup times, because when the program starts running the JIT compiler has to do analysis and compilation before the code can be executed. Studies have shown that many people will abandon an app if it takes more than a few seconds to start executing.

That’s the end of the background info. Wouldn’t it be awesome to combine the advantages of both AOT and JIT compilation? Read on.

Compiling and executing Dart

efore working on Dart, the Dart team members had done groundbreaking work on advanced compilers and virtual machines, both for dynamic languages (like the V8 engine for JavaScript and Strongtalk for Smalltalk) and for static languages (like the Hotspot compiler for Java). They used this experience to make Dart unusually flexible in how it can be compiled and executed.

Dart is one of very few languages (and perhaps the only “mainstream” language) that is well suited to being compiled both AOT and JIT. Supporting both kinds of compilation provides significant advantages to Dart and (especially) Flutter.

JIT compilation is used during development, using a compiler that is especially fast. Then, when an app is ready for release, it is compiled AOT. Consequently, with the help of advanced tooling and compilers, Dart can deliver the best of both worlds: extremely fast development cycles, and fast execution and startup times.

Dart’s flexibility in compilation and execution doesn’t stop there. For example, Dart can be compiled into JavaScript so it can be executed by browsers. This allows code reuse between mobile apps and web apps. Developers have reported as high as 70% code reuse between their mobile and web apps. Dart can also be used on a server either by being compiled to native code, or by compiling to JavaScript and using it with node.js.

Finally, Dart also provides a standalone VM that uses the Dart language itself as its intermediate language (essentially acting like an interpreter).

Dart can be efficiently compiled AOT or JIT, interpreted, or transpiled into other languages. Not only is Dart compilation and execution unusually flexible, it is especially fast.

The next section provides an example of how Dart’s compilation speed can be a game changer…

Stateful hot reload

One of the most popular features of Flutter is its extremely fast hot reload. During development, Flutter uses a JIT compiler that can reload and continue executing code usually in under a second. App state is retained across reloads whenever possible, so the app can continue from where it left off. state-hot-reload

It is hard to appreciate how important really fast (and reliable) hot reload can be during development, unless you have experienced it yourself. Developers report that it changes the way they create their apps, describing it as being like painting their app to life.

Flutter’s hot reload makes it far easier to try new ideas or experiment with alternatives, providing a huge boost to creativity.

So far, we’ve discussed how Dart makes things better for the developer. The next section is about how Dart also makes it easier to create smooth apps that delight users.

Avoiding jank

A fast app is great, but a smooth app is even better. Even a super fast animation will look bad if it is jerky. However, preventing jank can be difficult because there are so many different causes. Dart has a number of features to avoid many of the common things that cause jank.

Of course, (like any language) it is still possible to write a janky app in Flutter; Dart helps by being more predictable and giving the developer more control over the smoothness of their app, making it easier to provide the best user experience possible, bar none.

Running at 60 fps, user interfaces created with Flutter perform far better than those created with other cross-platform development frameworks. And not just better than cross-platform apps, but as good as the best native apps:

The UI is butter smooth… I have never seen such a smooth Android app.

AOT compilation and the “bridge”

We’ve already discussed one feature that helps keep things smooth, and that is Dart’s ability to be AOT compiled to native machine code. Pre-compiled AOT code is more predictable than JIT because there are no pauses during runtime to perform JIT analysis or compilation.

However, there is an even bigger advantage to AOT compiled code and that is avoiding the “JavaScript bridge”. When dynamic languages (like JavaScript) need to interoperate with native code on the platform, they have to communicate over a bridge, which causes context switches that have to save a particularly large amount of state (potentially to secondary storage). These context switches are a double whammy because they not only slow things down, they can cause serious jank.

the bridge

Note: even compiled code may need an interface to talk to platform code, and this can also be called a bridge, but it is typically orders of magnitude faster than the bridge required by a dynamic language. In addition, because Dart allows things like widgets to be moved into the app, the need to go over a bridge is reduced.

Preemptive scheduling, time slicing, and shared resources

Most computer languages that support multiple concurrent execution threads (including Java, Kotlin, Objective-C, and Swift) use preemption to switch between threads. Each thread is allocated a “slice” of time to execute, and if it exceeds its allocated time the thread is preempted using a context switch. However, if the preemption occurs when a resource that is shared between threads (like memory) is being updated, then this causes a race condition.

Race conditions are a double whammy because they can cause serious bugs, including crashing your app and causing data to be lost, and they are particularly difficult to find and fix because they depend on the relative timing of independent threads. It is all too common for race conditions to stop manifesting themselves when you run the app in a debugger.

The typical way to fix a race condition is to protect the shared resource using a lock that prevents other threads from executing, but locks themselves can cause jank, or even more serious problems (including deadlock and starvation).

Dart took a different approach to this problem. Threads in Dart, called isolates, do not share memory, which avoids the need for most locks. Isolates communicate by passing messages over channels, which is similar to actors in Erlang or web workers in JavaScript.

Dart, like JavaScript, is single threaded, which means it does not allow preemption at all. Instead, threads explicitly yield (using async/await, Futures, or Streams). This gives the developer more control over execution. Single threading helps the developer ensure that critical functions (including animations and transitions) are executed to completion, without preemption. This is often a big advantage not just for user interfaces, but for other client-server code.

Of course, if the developer forgets to yield control, this can delay other code from executing. However, we have found that forgetting to yield is usually much easier to find and fix than forgetting to lock (because race conditions are difficult to find).

Allocation and garbage collection

Another serious cause of jank is garbage collection. Indeed, this is just a special case of accessing a shared resource (memory), which in many languages requires the use of locks. But locks might stop the entire app from running while free memory is collected. However, Dart can perform garbage collection almost all of the time without locks.

Dart uses an advanced generational garbage collection and allocation scheme, which is particularly fast for allocating many short-lived objects (perfect for reactive user interfaces like Flutter that rebuild the immutable view tree for every frame). Dart can allocate an object with a single pointer bump (no lock required). Once again, this results in smooth scrolling and animation, without jank.

Unified layout

new Center(child:
  new Column(children: [
    new Text('Hello, World!'),
    new Icon(Icons.star, color: Colors.green),
  ])
)

A view in Dart and what it produces

Note that now that Flutter uses Dart 2, layout has become even simpler and clearer because the new keyword is optional, so static layouts can look even more like they are written in a declarative layout language, like this:

Center(child:
  Column(children: [
    Text('Hello, World!'),
    Icon(Icons.star, color: Colors.green),
  ])
)

However, I know what you are probably thinking — how can the lack of specialized layout languages be called an advantage? But it is actually a game changer. Here’s what a developer wrote in an article titled “Why native app developers should take a serious look at Flutter”.

In Flutter, layouts are defined using Dart code only. There is no XML / templating language. There’s no visual designer/storyboarding tool either. My hunch is, upon hearing this, a number of you might even cringe a little. Prima facie, that was my reaction too. Isn’t it easier to do layouts using a visual tool. Wouldn’t writing all kinds of constraint logic in code make things overly complicated? The answer for me turned out to be no. And boy! what an eye opener it has been. The first part of the answer is the hot reload mentioned above.

I can’t stress enough how this is light years ahead of Android’s Instant Run, or any similar solution. It just works, even on large non-trivial apps. And it is crazy fast. That’s the power of Dart for you.

In practice, that makes a visual editor interface redundant. I did not miss XCode’s rather nice auto layout at all. Dart creates terse and easy to understand layout, while the “crazy fast” hot reload lets you see the results instantly. And that includes the non-static parts of your layout.

And as a result, I have been way more productive writing layouts in Flutter (Dart) than either Android/XCode. Once you get the hang of it (for me that meant a couple of weeks), there is a substantial overhead reduction because of very little context switching that is happening. One does not have to switch to a design mode, and pick a mouse and start clicking around. And then wondering if something has to be done programmatically, how to achieve that etc. Everything is programmatic. And the APIs are very well designed. It becomes intuitive soon and is much more powerful than the constructs offered by auto layout / layout XMLs.

For example, here is a simple list layout that adds a divider (horizontal line) between every other item, defined programmatically:

return new ListView.builder(itemBuilder: (context, i) {
  if (i.isOdd) return new Divider(); 
  // rest of function
});

In Flutter, all layout exists in one place, regardless of whether it is static layout or programmatic layout. And new Dart tools, including the Flutter Inspector and the outline view (which take advantage of all layout being in one place) make complex, beautiful layouts even easier.

Is Dart a proprietary language?

No, Dart (like Flutter) is completely open source with a clean license, and is also an ECMA standard. Dart is popular inside and outside of Google. Inside Google it is one of the fastest growing languages and is used by Adwords, Flutter, Fuchsia and others; outside, the Dart repository has more than 100 external committers.

An even better indicator of the openness of Dart is the growth of the community outside of Google. For example, we are seeing a steady stream of articles and videos on Dart (including Flutter and AngularDart) from third parties, a few of which I’ve quoted in this article.

In addition to the external committers to Dart itself, there are over 3000 packages in the public Dart package repository, including libraries for Firebase, Redux, RxDart, internationalization, encryption, databases, routing, collections, and more.

Will Dart programmers be hard to find?

f not many programmers know Dart, will it be more difficult to find qualified programmers? The answer is an unequivocal no. Dart is an incredibly easy language to learn. In fact, programmers who already know languages like Java, JavaScript, Kotlin, C#, or Swift can start programming in Dart almost immediately.

Here’s how one programmer put it in an article titled “Why Flutter Will Take Off in 2018”:

Dart, the language used to develop Flutter apps, is stupid-simple to learn. Google has experience in creating simple, well documented languages like Go, for example. So far, to me, Dart reminds me of Ruby and it’s a pleasure to learn. It’s also not only for mobile but for the web as well. From another article about Flutter and Dart, titled “Why Flutter? and not framework X? or better yet why i’m Going Flutter all in.”

Flutter uses the Dart Language that was created by google also, to be honest i’m not a fan of strongly typed languages like C# or JAVA, but i don’t know why Dart’s way of writing code seems different. And i feel very comfortable writing it. Maybe because its very simple to learn, and very straightforward. Dart was specifically designed to be familiar and easy to learn, through extensive UX research and testing. For example, in the first half of 2017 the Flutter team did a UX study with eight developers. We gave them a short introduction to Flutter, then turned them loose for an hour or so creating a simple view. All of the participants were able to start programming right away, even though they had never used Dart before. They were focused on writing reactive views, not the language. Dart just worked.

At the end, one participant (who had progressed particularly far in the task) hadn’t mentioned anything about the language, so we asked them if they realized what language they were using. They didn’t know. The language didn’t matter; they were programming in Dart in minutes.

The hard part about learning a new system is typically not learning the language, it is learning all the libraries, frameworks, tools, patterns, and best practices for writing good code. And the Dart libraries and tools are exceptionally good and well documented. One article proclaims, “As a bonus, they also keep tremendous care of their codebase and they have the best documentation I’ve ever seen.” What little effort is spent learning Dart is easily made up for by the savings in time learning the rest.

As direct evidence, a large project inside of Google wanted to port their mobile app to iOS. They were about to hire some iOS programmers but instead decided to try Flutter. They monitored how long it took to get developers up to speed on Flutter. Their results showed that a programmer could learn Dart and Flutter and become productive in three weeks. This compares to the five weeks they had previously observed to get programmers up to speed on Android alone (not to mention that they would have had to hire and train developers for iOS).

Finally, the article “Why we chose Flutter and how it’s changed our company for the better” is from a company that moved their large enterprise application to Dart on all three platforms (iOS, Android, and web). Their conclusions:

Much easier to hire. We now look to take the best candidate regardless if they are from Web, iOS or Android. We have 3x the bandwidth now that all of our teams are consolidated on a single code base. Knowledge sharing is at an all time high They were able to triple their productivity by using Dart and Flutter. This should be no surprise given what they were doing before. They, like many companies, were building separate apps for each platform (web, iOS and Android) utilizing separate languages, tools, and programmers. Switching to Dart meant that they no longer had to hire three different kinds of programmers. And it was easy for them to move their existing programmers to Dart.

They and others have found that once programmers start using Flutter, they often fall in love with Dart. They like the terseness of the language, and the lack of ceremony. They love language features such as cascades, named parameters, async/await, and streams. And above all else, they love the features of Flutter (like hot reload) that are made possible by Dart, and the beautiful, performant apps Dart helps them build.

Dart 2

As this article is being published, Dart 2 is being released. Dart 2 is focused on improving the experience of building client apps, including developer velocity, improved developer tooling, and type safety. For example, Dart 2 features a sound type system and type inferencing.

Dart 2 also makes the new keyword optional. This means that it is possible to describe many Flutter views without using any keywords at all, making them less cluttered and easier to read. For example:

Widget build(BuildContext context) =>
  Center(child:
    Column(children: [
      Text('Hello, World!'),
      Icon(Icons.star, color: Colors.green),
    ])
  )

Dart 2 also uses type inferencing to make many uses of the const keyword optional by not requiring const to be specified redundantly inside of a const context. For example, the statement:

const breakfast = {
   const Doughnuts(): const [const Cruller(), const BostonCream()], 
};

can now be replaced by this:

const breakfast = {
   Doughnuts(): [Cruller(), BostonCream()],
};

Because breakfast is const, everything else is inferred to be const as well.

The secret is focus

The improvements in Dart 2 are focused on optimizing client-side development. But Dart will still be a great language to build server-side, desktop, embedded systems, or other programs.

Focus is a good thing. Pretty much all enduring popular languages benefitted from being very focused. For example:

  • C was a system programming language for writing operating systems and compilers. It became so much more.
  • Java was a language designed for embedded systems.
  • JavaScript was a scripting language for web browsers (!).
  • Even much-maligned PHP succeeded because it was focused on writing Personal Home Pages (where it got its name). On the other hand, many languages have explicitly tried (and failed) to be completely general purpose, such as PL/1 and Ada, among others. The most common problem is that without focus, these languages became proverbial kitchen sinks.

Many of the features that make Dart a great client-side language also make it a better language for use server side. For example, the fact that Dart avoids preemptive multi-tasking gives it the same advantages as Node on the server, but with much better and safer typing.

The same thing goes for writing software for embedded systems. Dart’s ability to handle multiple concurrent inputs reliably is key here.

Finally, Dart’s success on the client will inevitably generate more interest in using it on the server — just like what happened with JavaScript and Node. Why force people to use two different languages to build client-server software?

Conclusion

This is an exciting time for Dart. People who use Dart love it, and the new features in Dart 2 make it an even more valuable addition to your arsenal of tools. If you have not used Dart, I hope this article has provided you with valuable information about what is new or different about Dart, and that you will give it — and Flutter — a try.