secture & code

What makes Dart unique? What do you need to know?

This is not your typical article that teaches you how to learn a new programming language, like Dart. Instead, I want to share the features that really surprised and pleased me when I started working with this language.

Introduction

Dart is an open source programming language developed by Google that is already 14 years old! - at the time I'm writing this - loaded with a lot of features: object-oriented, with garbage collection, inspired by C..., moreover, Dart can compile not only native code for your favorite operating system but also for WebAssembly, JavaScript, Android and iOS applications (with the help of Flutter). Dart is a really interesting programming language.

Features

Okay, okay. Let's skip the preamble and get to what we really came here to read: what are the things that make it unique? Here is a list of the things that I have seen that I liked. The order is almost random, but ok, ready first the ones I like the most.

Let's get to it!

Rethrow

Yes, right, Dart supports exceptions. If something has gone wrong, we can throw Exception(«Something has gone wrong!») and it will behave just as you may have guessed. But do we know what the programming language does when handling exceptions? If you don't know, I'll explain it quickly. Every time an exception is thrown, it is stored in the exception stack, until one of the following happens: we handle them with a try/catch or... well... the application blows up in our face. That's when we see the exception trace. Many programming languages are accumulating the information of all the exceptions caught and thrown. For example Java:

java.lang.RuntimeException: Final exception
   at com.example.MyClass.methodD(MyClass.java:40)
   at com.example.MyClass.methodA(MyClass.java:10)
   at com.example.Main.main(Main.java:5)
Caused by: java.lang.IllegalStateException: Intermediate exception
   at com.example.MyClass.methodC(MyClass.java:30)
   at com.example.MyClass.methodB(MyClass.java:20)
Caused by: java.lang.IllegalArgumentException: Original exception
   at com.example.MyClass.methodE(MyClass.java:50)

This is not a bad thing, but these traces can accumulate and then it becomes a string of text that no one can read.

With rethrow we can relaunch the exception we have captured, without adding it to the stack of traces; with this we will have more reduced and concrete traces. Let's see an example:

void main() {
 try {
   divideNumbers(10, 0);
 } catch (e) {
   print('Caught error in main function: $e');
 }
}


void divideNumbers(int a, int b) {
 try {
   if (b == 0) {
     throw Exception('Division by zero');
   }
   print(a / b);
 } catch (e) {
   print('We caught a bug: $e');
   rethrow;              <-- We capture the error and propagate it.
 }

Solid nullity assurance

The security of solid nullity (or sound null safety is Dart's ability to ensure that variables cannot be null unless we specify that explicitly. Dart makes compile-time checks to enforce this rule, avoiding null assignment errors at runtime.

String? name;  // Notice the question mark!


name = null;   // name allows the assignment of nulls...
name = "John"; // ... and obviously also of text strings


print(name);

First-class functions

First-class functions are “first-class citizens” in Dart, and this means that functions can:

  • Be assigned to variables.
  • Be passed as arguments to other functions.
  • To be used as return in the functions.
  • And be stored in data structures such as lists or maps!

This allows us flexibility in our code, improving its maintainability:

void main() {
 // Define a function that takes another function as parameter
 void performOperation(int a, int b, int Function(int, int) operation) {
   int result = operation(a, b);
   print('The result is 1TP4Result');
 }


 // Define a simple function
 int add(int x, int y) => x + y;


 // Pass function as argument
 performOperation(5, 3, add);


 // Use an anonymous function as argument
 performOperation(5, 3, (x, y) => x * y);
}

Statically typed language... or not?

That yes, I understand that we can talk about this feature for hours without reaching an agreement. But at least Dart offers us the ability to type our code with:

  • Numbers:
    • int for integers, or
    • double for numbers with decimals.
  • Text strings with String.
  • Boolean values with bool.
  • Arrays... well in Dart they are called List.
  • Set, a collection of unique data.
  • Map, data set with an index.
  • Even variables that can store whatever you want, with dynamic.
void main() {
 // Integer (int)
 int age = 30;


 // Decimal number (double)
 double height = 1.75;


 // Text string (String)
 String name = "John";


 // Boolean value (bool)
 bool esStudent = true;


 // List (List)
 List<String> fruits = ['apple', 'banana', 'orange'];


 // Set (Set)
 Set<int> numerosUnicos = {1, 2, 3, 3};


 // Map
 Map<String, String> capitals = {'Spain': 'Madrid', 'France': 'Paris'};


 // Dynamic (dynamic)
 dynamic variableDynamics = 'It can be anything';
 variableDynamics = 42;
}

Optional function parameters

Function parameters in Dart can be optional. For example, let's take this start of a function with optional parameters:

void describeAnimal(String name, [String? species, int? age]) {

You will see that the species and age parameters are optional. You will know this because they are inside square brackets. In addition, you will also notice that both can be null; this is because if the function is called in this way...

describeAnimal('Lion');

...both species and age shall be null and void.

To call them with data, the function must be called in this way:

describeAnimal(Michi, 'Cat', 3);

Then species will be Cat and age will be 3.

Finally, we could assign them default values, so that if the function is called without specifying these two parameters, they will have a value other than null:

void describeAnimal(String name, [String species='Cat', int age=3]) {

Named function parameters

This type of parameter also comes in handy at certain times. It is basically that the function parameters can have a name assigned to them and do not have to be called in the same order in which they were defined in the function.

Look at this code snippet:

main() {
 greet(name: 'John');
 greet(title: 'Mr.', name: 'John');
}


void greet({required String name, String? title}) {
 print('Hello, $itle 1TP4Name');
}

Here we define a function that accepts two parameters, a name, which is required, and a title, which is optional. In fact, all named parameters of a function are optional by default.

Notice that when we call the function the first time, we omit the value for title, so it will be null. The result of the first call will be: Hello, null John

But look carefully the second time we call the greet function. You will see that positionally we write the title first and the name after, totally different from how they were defined in the function, but Dart doesn't care about this and will show us: Hello, Mr. John

Cool!

Factory builders

This type of constructors are special constructors that return an instance of the class, an instance already created or a subtype of an instance. Factory constructors are used to implement singletons, The following are some of the most important features of the program: - Caching and creation of conditional objects.

Now pay attention to this, which will make your head explode 🤯:

class Article {
 final String name;
 final bool esBorrador;


 // Factory builder for draft articles
 factory Article.Draft(String name) {
   return Article(name, true);


 }


 // Factory builder for published articles
 factory Article.Published(String name) {
   return Article(name, false);
 }


 // Traditional... builder
 Article(this.name, this.esDelete);
}


void main() {
 Article draft = Article.Draft('Test article');
 Article published = Article.Published('Definitive article');
 Article customized = Article('Customized article', true);
 
 // Test article, true
 print("${draft.name}, ${draft.name}, ${draft.enDraft}");


 // Final article, false
 print("${published.name}, ${published.name}, ${published.name}, ${published.name}, ${published.name}.");


 // Customized article, true
 print("${personalized.name}, ${personalized.name}, ${personalized.name}, ${personalized.name}, ${personalized.name}.");
}

In this code snippet we define three constructors: two factory constructors Article.Draft y Article.published, and the traditional builder, the one that has been around forever. Factory builders have business logic inside and, when they finish defining it, they call the traditional builder. If this doesn't convince you to try Dart, then nothing will.

dart

Last impressions

As you could see, Dart is a powerful, modern language on steroids. There are more things that I liked, like the mixins (a way to share code between classes like PHP traits), multi-thread management, the keyword beats for variables and much more.

I hope you liked the article and that the examples I have given have helped you to understand a little better the different attributes of Dart that I liked the most. Did you miss our last article? Read it! here!

Full-Stack

Picture of Gonzalo Payo

Gonzalo Payo

Enthusiastic Fullstack Programmer who, at the same time, likes to write.
Picture of Gonzalo Payo

Gonzalo Payo

Enthusiastic Fullstack Programmer who, at the same time, likes to write.

We are HIRING!

What Can We Do