Dart Tips

December 29, 2018

I have been playing with the Dart language a lot recently and here are some tips which I find very useful!

Trailing Commas for Better Auto-Formatting

The Dart team recommends us to always use the dartfmt tool to format our code into a standard code style (like gofmt with Golang). If you are from the JS land, dartfmt is like Prettier but even more opinionated, with exactly zero configurable options available.

This might not sound so fun if you come from another C-like language and are used to a drastically different coding style than the one promoted by the Dart team. However by enforcing the standard style through out the community, reading or maintaining the code written by others can become much easier. The war of tabs and spaces can finally be put to an end (looks like team-spaces wins!).

However, the formatter might seem to “misbehave” in situations like the one illustrated below - notice how the closing parens get all piled up on the second-to-last line. This makes it very difficult to generally make sense of which closing parenthesis belongs to which part, not to mention it has become super hard now to select and move pieces of code around. Looks like a bug of dartfmt! 😅

  
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: Text(widget.title)),
        body: Center(
            child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
              Text('Without trailing commas be like...')
            ])));
  }

The truth, however, is that dartfmt uses trailing commas (which are optional in Dart) to give users some degrees of liberty decide how the indentation should be auto-formatted. One aesthetically better way to reformat the code above is to simply use trailing commas everywhere when it makes sense - if we are nesting multiple widget constructor calls within each other (like a normal person does with Flutter), it would be a good idea to ask dartfmt to place the closing parentheses on newlines like closing tags in HTML; on the other hand if we are just making a short function call, we’d prefer the parameters to be put on the same line.

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'With trailing commas be like...',
            ),
          ],
        ),
      ),
    );
  }

?. for More Concise Null Checking

In recent years, the “conditional member access” operator (?.) has been gradually added to many C-like languages like Groovy or C#. Proposals are also being made for JavaScript. This extended dot operator (.) saves us from manually doing the explicit null-checking every layer when navigating down an object graph.

Well, Dart has this handy little operator too! Consider the following example:

/// Without `?.`
if (awe != null) {
  if (awe.some != null) {
    if (awe.some.ness != null) {
      awe.some.ness();
    }
  }
}

/// With `?.`
awe?.some?.ness();

Pretty neat!

The Cascade Notation

Dart has another handy extension to the . operator - the double dot (..) “cascade”.

One use of the cascade “operator” is to save us some typing when multiple method calls on a same object are being made, for example:

/// Without `..`
sadie.race = 'Dog';
sadie.age = 6;
sadie.runs();
sadie.barksAt(bailey);

/// With `..`
sadie
  ..race = 'Dog'
  ..age = 6
  ..runs()
  ..barksAt(bailey);

It is also common to use cascade in order to “chain” together otherwise unchainable method calls which return void themselves.

List<Dog> dogs = []

/// Without `..`
dogs.add(sadie);
dogs.add(bailey);
dogs.add(cooper);

/// [List#add] is a mutable method. `dogs.add(duke)` returns `void` and can't be used by `.add(rocky)`.
dogs.add(duke).add(rocky) // => Error

/// We can simulate "chaining" with `..`, as the code below is basically equivalent to:
/// ```
/// dogs.add(duke);
/// dogs.add(rocky);
/// ```
dogs
  ..add(duke)
  ..add(rocky); 

Pretty useful in OOP! 😃