When reading articles comparing React Native and Flutter you may often encounter a statement that the great advantage of the former technology is the fact that it uses JavaScript. This is a mainstream language so there’s a lot of experienced developers out there. On the other hand, Flutter uses Dart which is not that popular. Obviously it’s more difficult to find experts in this technology. The truth is that most Android developers already know Dart … but they don’t realize it yet!

Why Flutter uses Dart

In 2017 Google announced Kotlin as the official language for the Android platform, but for Flutter the company have chosen an entirely different solution. One of the main goals for Flutter designers was to increase programmers’ productivity. Those of you who have developed Android applications in the past, knows how much time it takes to apply changes in the code to a smartphone or an emulator. In this case Dart language seemed perfect, because its runtime can work in two compilation modes: JIT (just-in-time) and AOT (ahead-of-time).

A JIT compiler runs during the execution of a program and compiles on the fly. This significantly shortens programming cycles, accelerates the whole process and, most importantly, allows hot reload. Thanks to this, you are able to see the output of your code immediately. However, this is not a perfect solution, because JIT compilation may cause delays or lags during the runtime.

But Dart also allows AOT compilation, which results in a faster, more predictable and stable program execution. Unfortunately, it results in much longer programming cycles.

Dart is one of a few languages that is designed for being able to compile both JIT and AOT. Supporting both kinds of compilation is a great advantages of Dart and Flutter itself. Thanks to this, mobile applications made with Flutter have very short programming cycles, as well as a native applications’ performance.

How to start using Dart

No IDE is necessary to start writing Dart code, all you have to do is go to https://dartpad.dartlang.org/ website and you are good to go. Obviously, if you are going to create something more complicated than sample applications, you should think of one. There are Dart plugins for VS Code as well as Android Studio or IntelliJ. 

Basic syntax

Let’s start with defining the second most popular class, right after Employee, namely Account:

class Account {
  String id;
  String name;
  double balance;
}

void main() {
  var account = Account();
}

Keynotes:

  • The main function, which is the application’s entry point, is not defined inside any class. In Dart variables, functions and properties can live at the top level.
  • To create an instance of a class you don’t need to use new operator, although you can,
  • the variables’ types can be inferred, but also can be declared explicitly: Account account = Account();.
  • Using semicolons is mandatory.
  • Dart creates a public, argument-less constructor when you don’t define other constructor.
  • in the real world application you should never use double type to represent money because of computation precision.

Encapsulation

Unfortunately, the Account class defined in this way will be quite useless. You definitely don’t want to let clients to change the id or manipulate the balance directly, so it’s time to make the code more object oriented.

class Account {
  final String id;
  String name;
  double _balance = 0;
  double get balance => _balance;
  
  Account(this.id, this.name);
}

var account = Account('1234-5678-9000', 'Personal Account');

Keynotes:

  • This keyword on the constructor’s parameters list allows automatic assignment of the parameter to the corresponding property and is equivalent to: 
    Account(String id, String name) {  
      this.id = id;
      this.name = name;
    }
  • There are no keywords defining access modifiers, everything is public by the default, unless its name starts with an underscore, which is used to mark an identifier as  private.
  • Dart support properties, so there’s no need to create getters and setter manually. You can do that if you want to change the default behavior.
  • To define a  String you can use a quotation as well as an apostrophe.
  • Everything is an object, including numbers or functions. There are no primitive types. That means that numeric values have to be initialized with a value, otherwise they will contain null.

Named parameters

In Java it’s very common to use the builder pattern when the number of constructor’s parameters is getting too big. Luckily, Dart allows to define named parameters and also to assign default values. Moreover you can mark required parameters with a special keyword:

import ‘package:meta/meta.dart’;

Account({
  @required this.id,
  this.name = '',
}) : assert(id != null && id.isNotEmpty, 'Id must be defined.');

var account = Account(
 id: '1234-5678-9000',
 name: 'Personal Account',
);

Keynotes:

  • @required annotation doesn’t result in a compilation error when no argument is passed, it only generates a warning.
  • assert() calls are ignored in the production code.

Methods

Now clients cannot modify the account balance directly. But to make this class useful you should provide methods that allow to deposit and withdraw funds from the account:

void deposit(double amount) {
  _balance += amount;
}

void withdraw(double amount) {
  _balance -= amount;
}

Keynotes:

  • Function definition doesn’t require any keyword.
  • It is possible to omit the return type
    deposit(double amount) {
      _balance += amount;
    }
  • Each function in Dart returns a value and when it’s not explicitly defined it returns null.

Error handling

The changing balance methods implementation is far from being perfect: a client can add funds to his account by withdrawing a negative amount. What’s more he is also capable of withdrawing more funds than he actually owns. 

void withdraw(double amount) {
  if (amount < 0) 
    throw ArgumentError('The withdraw amount value must be positive.');
  if (_balance - amount < 0) 
    throw InsufficientFundsException(balance: _balance, amount: amount);
    
  _balance -= amount;
}
class InsufficientFundsException implements Exception {
  final double balance;
  final double amount;

  InsufficientFundsException({
    this.balance,
    this.amount,
  });
}

Keynotes:

  • Errors represent all programming issues that are unexpected. It could be an invalid function argument or illegal state of an object. A client shouldn’t handle them and terminating a program is often the safest response.
  • Exceptions should provide an user with information about failure, so that the error can be addressed programmatically. They are intended to be caught, and it should contain useful data fields.
  • it is possible to use any kind of object to notify about an error, for example: throw ‘Your account balance does not allow you to perform this operation.’;

The future of Dart

When moving to Dart as a Java developer you will be surprised how fun and easy Dart is. It has numerous features that will make your life easier and allow you to write less code. What’s more, many people believe that Flutter is going to be the main tool for developing application for Fuchsia. It seems that it’s a great time to give Dart a try!