Author: Tan Yuanhong
Reviewers: Daryl Tan, Ang Ze Yu, Tejas Bhuwania
Dart (previously also known as dartlang
) is an object-oriented, a style of Object-oriented programming (OOP) in which inheritance occurs via defining classes of objects, instead of inheritance occurring via the objects alone (compare prototype-based programming)class defined, garbage-collected language using a C-style syntax that also known as source-to-source compilationtranscompiles optionally into JavaScript. It's rumored that Dart was invented out of Google engineers' frustration with JavaScript (they even built a version of Chromium browser with Dart VM so that Dart code can be run on the web without transcompiling to JavaScript). However, as most developers still stick to JavaScript and it turned out that TypeScript is a much more widely-accepted solution for statically typed JavaScript transcompilation, Dart is then, with the emergence of a cross-platform mobile UI framework developed by GoogleFlutter, re-purposed as a client-optimized language that's optimized for UI creation and cross-platform execution.
Dart is optimized for declarative UI. Dart's syntax allows you to write statically typed code in a JSON-like way, and writing asynchronous code is a breeze in Dart, both of which makes Dart an ideal choice for developing declarative UI.
For the reasons above, Dart is adopted by Flutter, a cross-platform mobile UI framework. (Why Flutter?) Not only you shall build Flutter apps using Dart, the Flutter framework itself is written in platform-dependent channel methods excluded, of course...pure Dart. In short, Dart is suitable for writing declarative UI, and Dart is a pre-requisite for Flutter.
A programming language usually needs to balance between development speed and performance. Dart is ambitious: it wants to run fast on all platforms (which requires compilation to native machine code) while allowing the developers to live reload code changes without re-compiling the entire file (which requires a VM of some kind). Thus, Dart has three execution modes for the best of all words:
The flexibility provided by the three execution modes of Dart makes Dart unique in the world of programming languages - there are few languages that maintains three components (i.e. VM, compiler and transpiler). It's a lot of work for the Dart maintainers, but saves a lot of work for Dart developers.
Asynchronous programming is important for UI development because you don't want your UI to freeze when some time-consuming operation (e.g. network request, computationally heavy subroutines) is happening.
There are three major ways Dart supports asynchronous programming
Future
object, which is very similar to JavaScript promiseStream
object, which can be "subscribed" toasync
and await
syntaxAsynchronous programming is commonly used in UI because certain operations are not instant, most common of which is HTTP requests:
import 'dart:html';
main(List<String> args) {
Future<String> respFuture = HttpRequest.getString('https://jsonplaceholder.typicode.com/todos/1');
respFuture.then((result) => print(result));
print("Hello world");
}
You should expect "Hello world" to be printed before the HTTP request's result because Future
object makes sure that the progress of main
will not be hindered by the HTTP request. Instead, callback function (result) => print(result)
is called whenever the result is ready - thus asynchronous.
However it's possible to enforce the "line-by-line" execution order by using await
in an async
function:
import 'dart:html';
main(List<String> args) async {
var result = await HttpRequest.getString('https://jsonplaceholder.typicode.com/todos/1');
print(result);
print("Hello world");
}
You may understand await
as "waiting for the result, do not proceed until it's ready". .then
and await
are two different ways of consuming the result of a Future
. To help you better understand the difference between async-await
and then
, here is an example:
Extension methods aim to solve one problem: when using an external library, you may want to extend or even change some of the methods for your own needs. For example, instead of using int.parse(42)
, you want to extend the String
class so that it has a method parseInt()
to parse a string to int. You can easily do so by extending String
class in Dart:
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
}
main(List<String> args) {
print('42'.parseInt());
}
Extending classes, especially core classes like String
, could be quite tricky if not impossible without extension methods. However, in Dart, you can easily customize the behavior of built-in classes like so. Extension methods in Dart have even more interesting stuff like generic support.
This is what makes declarative UI code written in Dart look surprisingly similar to JSON. In a language where the order of a function's parameters is specified by the function's definition, if you only look at the function call, it might be difficult for you to figure out what those parameters mean. One classic example is the put(key, value)
of a Map data structure - if you only see m.put(1, 2)
, can you tell which one is key without prior knowledge?
That's when named parameters become handy:
When calling a function, you can specify named parameters using paramName: value
. For example:
enableFlags(bold: true, hidden: false);
When defining a function, use {param1, param2, ...}
to specify named parameters:
/// Sets the [bold] and [hidden] flags ...
void enableFlags({bool bold, bool hidden}) {...}
Note that if named parameters are used, the order no longer matters, you can write enableFlags(hidden: false, bold: true);
as well. This feature is especially useful for declarative UI as the properties of a widget are, most of the time, parameters passed to the constructor of the object. Below is a concrete example in Flutter. Imagine how messy and frustrating the code would look like if the parameters are not named and we have to refer to the documentation for the exact order of them!
// Copyright 2018 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Welcome to Flutter',
home: Scaffold(
appBar: AppBar(
title: Text('Welcome to Flutter'),
),
body: Center(
child: Text('Hello World'),
),
),
);
}
}
Tooling support: Dart has out-of-the-box dependency management (pub.dev), linting solution (dartfmt), documentation generator (dartdoc) and official testing framework. After installing Dart SDK, you're ready to go 99% of the time.
Popularity: Dart is a relatively new (first appeared in 2011), but quickly gained popularity in recent years: it's ranked 16th the first time it entered the IEEE Spectrum programming language ranking, and 23rd on TIOBE as of April 2020. And Dart is being actively maintained by a team at Google: check out the GitHub repo of Dart SDK.
Of course, like any other language, Dart is not perfect.
Dart is well-documented and you should find solutions to most of your Dart problems on their website.
Like many devs who tried Dart says, you might already know Dart. If you already have any prior programming experience in any language (say, C, C++, Java, JavaScript, Python, Kotlin, Swift), you're pretty likely to master Dart within weeks for the following reasons:
get
and set
keywords for OOP, null safety syntax