Flutter Beta

Learn cross-platform development with our collection of trusted high-quality Flutter tutorials!

Dart Basics

Get an introduction to the basics of the Dart programming language, used for development with the Flutter SDK for mobile, web, and beyond.

4.9/5 16 Ratings

Version

  • Dart 2, Flutter, DartPad

You’re looking to get started with Flutter development, but have only just found out that Flutter uses the Dart programming language? This tutorial will introduce you to some Dart basics so you can get up and running with the language for Flutter development.

Dart was released by Google in 2011 and was initially designed as a replacement for JavaScript. Since then, the release of the Flutter SDK for iOS, Android, and web development has put a new spotlight on the Dart language. The most recent version of Dart at the time of this recording is 2.4.

Dart has many similarities to other languages you may already be familiar with, such as Java, C#, Swift, and Kotlin. Dart has the following language characteristics:

  • Statically typed
  • Type inference
  • String expressions
  • Multi-paradigm including OOP and functional
  • Can mimic dynamic types using dynamic

In addition to its use for mobile and web development, Flutter is the development kit for Fuchsia, an experimental operating system under development at Google.

This tutorial will introduce you to Dart basics such as:

  • Variables, data types, and operators
  • Conditionals and loops
  • Functions, anonymous functions, and closures
  • Arrow function syntax

You can go beyond the basics and learn more about Object-Oriented Programming in Dart as well as Dart collection types such as lists, sets, and maps, in our course Programming in Dart – Fundamentals, on which this short tutorial is based.

After completing the tutorial, you should be ready to dive right into Flutter development using Dart. You can get started with Flutter here.

Getting Started

To get up and running quickly for learning Dart, you’re best bet is to use the DartPad editor, found at https://dartpad.dartlang.org:

DartPad

DartPad is setup like a typical IDE. There is:

  • An Editor pane on the left
  • A Run button to run code in the editor
  • A Console on the upper right showing output
  • An Info panel on the bottom right showing information highlighted code
  • A Samples dropdown to show some sample code
  • A Share button you can use to share DartPad files you’ve created

Down in the lower bottom right, there’s a checkbox to show some more panels related to using Dart for web programming, and some text showing the current version of Dart being used in DartPad.

If you prefer, you can install the Dart SDK locally on your machine. One way to do so is to install the Flutter SDK. Installing flutter will also install the Dart SDK.

You can also just install the Dart SDK directly by visiting here.

Core Concepts

Dart programs begin with a call to the `main` function, and the syntax for `main` looks very similar to the syntax for `main` in other languages like C, Swift, or Kotlin.

Clear out all the code in the default DartPad, and add a `main` function to the editor:

void main() {
}

The main function is preceded by a return type, which in Dart is void, meaning nothing is returned. The parentheses after main indicate that this is a function definition, and the curly braces contain the body of the function. Inside main, you add the Dart code for your program.

Variables, Comments, and Data Types

The first thing we’ll add to main is a variable assignment statement. Variables hold the data that your program will work on. You can think of a variable like a box in your computer’s memory that holds a value. Each box has a name, the name for the variable. You use the var keyword in Dart to denote a variable.

Add a new variable to main:

var myAge = 35;  

Like C and Java, each Dart statement ends in a semicolon. You’ve created a myAge variable set it equal to 35.

You can use the built-in print function in Dart to print the variable to the console, so add that call after the variable:

print(myAge); // 35

Click the Run button in DartPad to run the code, and you’ll see the variable printed out in the console on the upper right.

Comments in Dart look just like those in C in other languages: text either following // on a line, or text within /* … */ blocks.

// This is a comment.
print(myAge); // This is also a comment.

/*
 And so is this.
 */

If you put the cursor in the editor over the myAge name, you’ll see in the lower right panel that Dart has inferred that myAge is an int variable, thanks to being initialized with the integer value 35. Like Swift and Kotlin, Dart uses type inference to attempt to figure out the type of your date if you do not specify it directly.

Type Inference

An alternative to using type inference is to replace the var keyword with the specific type you want to use:

int yourAge = 27;
print(yourAge); // 27

Similar to many languages such as C, Java, Swift, and Kotlin, Dart is statically typed. That means that each variable in Dart has a type that must be known when the code is compiled, and the type of the variable cannot change when the program is run.

This contrasts with languages like Python and Javascript that are dynamically typed, meaning variables can hold different kinds of data when being run, and the type does not need to be known when the code is compiled.

Basic Dart Types

Dart uses int for integers, double for floating points, and bool for booleans. ints and doubles are both derived from a type named num. You use the String type to represent sequences of characters. Dart also has a keyword dynamic which lets you mimic dynamic typing in the statically typed Dart.

Dart Data Types

You can also use type inference for types other than int. Enter a variable pi equal to 3.14 like so:

var pi = 3.14;
print(pi); // 3.14

pi is inferred to be a double since you used a floating-point value to initialize it. You can see that in the Dart info panel

You can also just use double as the type instead of var:

double c = 299792458;
print(c); // 299792458

In this case, you’ve initialized c, the symbol for the speed of light, with an int, but because you specified the type as double, c is in fact a double. Dart will convert the int to a double in order to initialize c. So unlike Swift, Dart has implicit type-conversion.

The dynamic Keyword

If you use the dynamic keyword instead of var, you get what is effectively a dynamically-typed variable:

dynamic numberOfKittens;

You can set numberOfKittens to a String using quotes (see below for more on the String type)

numberOfKittens = 'There are no kittens!';
print(numberOfKittens); // There are no kittens!

numberOfKittens has a type, as it must since Dart has static typing. But that type is dynamic, which means you can assign other values with other types to it. So you can assign an int value below your print statement.

numberOfKittens = 0;
print(numberOfKittens); // 0

Or if you have a kitten in Schrödinger’s box, you could assign a double value:

numberOfKittens = 0.5;
print(numberOfKittens); // 0.5

Schrödingers Cat

Go ahead and click Run to see the three different values for numberOfKittens all printed out in the console. In each case, the type of numberOfKittens remains dynamic, even though the variable itself holds values of different types.

The bool type is used to hold values of either true or false.

bool areThereKittens = false;
print(areThereKittens); // false

But, if you look inside the Schrödinger box, you may switch to having a real live kitten:

numberOfKittens = 1;
areThereKittens = true;
print(areThereKittens); // true

Run the code again to see your boolean values in the console. It’s a good thing you looked in the box! :]

Operators

Dart has all the usual operators you’re familiar with from other languages like C, Swift, and Kotlin.

There are arithmetic operators, equality, increment and decrement, comparison, and logical operators.

Dart also allows for operator overloading like C++ and Kotlin, but that’s beyond the scope of this tutorial.

The arithmetic operators work just liked you’d expect. Add a bunch of operations to your DartPad:

print(40 + 2); // 42
print(44 - 2); // 42
print(21 * 2); // 42
print(84 / 2); // 42

See more here on 42.

Answer to Life the Universe and Everything

You can initialize variables using arithmetic expressions:

var atltuae = 84.0 / 2;
print(atltuae); // 42

Dart converts the int to a double prior to the operation, so that the resulting variable is inferred to be a double.

Dart has double-equals equality and not-equals operators:

print(42 == 43); // false
print(42 != 43); // true

There are pre- and post-fix increment and decrement operators:

print(atltuae++); // 42
print(--atltuae); // 42

Because the use of increment is postfix, 42 is printed before the increment occurs. The decrement is prefix, so 43 is decremented to 42 and then the value 42 is printed.

Dart has the typical comparison operators like less than and greater than or equal to.

print(42 < 43); // true
print(42 >= 43); // false

There are also the usual compound arithmetic/assignment operators

atltuae += 1; print(atltuae); // 43
atltuae -= 1; print(atltuae); // 42
atltuae *= 2; print(atltuae); // 84
atltuae /= 2; print(atltuae); // 42

And Dart has the usual modulo operator.

print(392 % 50); // 42

The logical operators such as && for and and || for or look just like those from other languages.

print((41 < atltuae) && (atltuae < 43)); // true
print((41 < atltuae) || (atltuae > 43)); // true

And the negation operator is the exclamation mark, turning false to true and true to false.

print(!(41 < atltuae)); // false

Strings

The Dart string type is String. Strings are expressed in Dart using text surrounded by either single or double quotes.

Like for the other types we've seen, you can use either var and type inference or String to create a string variable:

var firstName = 'Albert';
String lastName = "Einstein";

Similar to languages like Kotlin and Swift, you can embed values and expressions inside strings to create new strings, using the dollar sign symbol $.

var physicist = "$firstName $lastName";
print(physicist); // Albert Einstein

You can combine adjacent strings, for example, long strings that go multiple lines, simply by placing the strings next to one another or on separate lines:

var quote = 'If you can\'t' ' explain it simply\n'
  "you don't understand it well enough.";
print(quote);
// If you can't explain it simply
// you don't understand it well enough.

Here in the first string, you used single quotes and so used an escape sequence \' to embed a quotation mark for can't into the string. The escape sequences used in Dart are similar to those used in other C-like languages, for example, \n for a newline.

Since you used double-quotes to delimit the second string, you did not need an escape sequence for the single quote on don't.

You can also combine strings using the + operator:

var energy = "Mass" + " times " + "c squared";
print(energy); // Mass times c squared

You can use triple quotes to have a string run multiple lines and preserve formatting:

var model = """
I'm not creating the universe.
I'm creating a model of the universe,
which may or may not be true.""";
print(model);
// I'm not creating the universe.
// I'm creating a model of the universe,
// which may or may not be true.

If you need to have escape sequences shown within the string, you can use raw strings, which are prefixed by r.

var rawString = r"If you can't explain it simply\nyou don't understand it well enough.";
print(rawString); 
// If you can't explain it simply\nyou don't understand it well enough.

Go ahead and click Run in DartPad to see all your strings in the console.

Immutability

Dart has the keywords const and final for values that don't change. const is used for values that are known at compile-time, final is used for values that don't have to be known at compile-time but cannot be re-assigned after being initialized. final acts like val in Kotlin or let in Swift.

You can use const and final in place of var and let type inference determine the type:

const speedOfLight = 299792458;
print(speedOfLight); // 299792458

So speedOfLight is inferred to be an int, as you can see in the info panel of DartPad.

final means immutable, and final values cannot be re-assigned. You can also explicitly state the type with either final or const:

final planet = 'Jupiter';
// final planet = 'Mar'; // error: planet is immutable
final String moon = 'Europa';
print('$planet has a moon $moon');
// Jupiter has a moon Europa

Nullability

For any variable, no matter the type, if you do not initialize the variable, the variable will be given the value null, which means nothing is stored in the variable.

This is in contrast to languages like Swift and Kotlin, for which you have to explicitly declare a variable as being nullable (aka optional in Swift). Default types in those languages are non-nullable.

It turns out that all Dart types, even basic types like int and double, are derived from a type named Object. And if you don't initialize an object, it takes on a null value.

You'll see here some ways to handle null values safely in Dart, but it does not go quite as far as optionals in Swift or nullables in Kotlin.

Create three variables of different types and then immediately print them.

int age;
double height;
String err;
print(age); // null
print(height); // null
print(err); // null

You'll see that each of the variables is null in the console.

Dart has some null-aware operators you can use for working with null values.

The double-question mark operator ?? is like the "Elvis operator" in Kotlin: it will return the left-hand operand if it's not-null, otherwise the right-hand value:

var error = err ?? "No error";
print(error); // No error

There's a corresponding assignment operator ??= that does similar:

err ??= error;
print(err); // No error

Since you just made error non-null with a value "No error", the err value now picks up that non-null value on this assignment.

There's also an operator ?. that protects you from accessing properties on null objects. It will return null if the object itself is null. Otherwise, it returns the value of the property on the right-hand side:

print(age?.isEven); // null

If you tried just age.isEven and age was null, you'd get an Uncaught exception.

Control Flow

Control flow lets you dictate that certain lines of code are executed, skipped over, or repeated. Control flow is handled in Dart with conditionals and loops.

Conditionals

The most basic form of control flow is deciding whether to execute or skip over certain parts of your code, depending on conditions that occur as your program runs. The language construct for handling conditions is the if/else statement. if/else in Dart looks nearly identical to the use in other C-like languages.

Suppose you have an animal variable that's currently a fox.

var animal = 'fox';

Fox

You can use an if statement to check whether the animal is a cat or dog, and run some corresponding code if so.

if (animal == 'cat' || animal == 'dog') {
  print('Animal is a house pet.');
}

Here you've used the equality and or operators to create a bool value inside the condition for the if statement.

You can use an else clause to run alternative code if the condition is false:

} else {
  print('Animal is NOT a house pet.');
}
// Animal is NOT a house pet.

You can also combine multiple if/else statements into an if/else if/else construct:

if (animal == 'cat' || animal == 'dog') {
  print('Animal is a house pet.');
} else if (animal == 'rhino') {
  print('That\'s a big animal.');
} else {
  print('Animal is NOT a house pet.');
}
// Animal is NOT a house pet.

You can have as many else if branches in between if and else as needed.

While Loops

Loops let you repeat code a certain number of times or based on certain conditions. The latter are handled by while loops.

There are two forms of while loop in Dart, while and do-while. The difference is that for while, the loop condition is before the code block, and in do-while the condition is after. So for do-while, the code block is guaranteed to run at least one time.

Create a variable i initialized to 1:

var i = 1;

You can use a while loop to print i while incrementing it, and set the condition to be that i is less than 10:

while (i < 10) {
  print(i);
  i++;
}
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9

Run the code and you see the numbers 1 to 9 printed out by the while loop.

Reset i in DartPad and then add a do-while loop:

i = 1;
do {
  print(i);
  ++i;
} while (i < 10);
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
// 9

The results here are the same as before, just this time, the loop body ran once before the loop exit condition was checked.

continue and break

Dart has the usual continue and break keywords for use in loops and elsewhere. continue will skip remaining code inside a loop and immediately go to the next iteration. break stops the loop and continues execution after the body of the loop.

You must be careful when using continue in your code. For example, if you take the do-while loop from above, and say you want to continue when i is equal to 5, that could result in an infinite loop that just keeps running, depending in where you place the continue statement:

do {
  print(i);
  if (i == 5) {
    continue;
  }            
  ++i;
} while (i < 10);
// 1
// 2
// 3
// 4
// 5
// 5
// 5
// 5
// 5
// 5
// 5
// 5
// 5
// 5
// ...

The infinite loop occurs since once i is 5, you never increment i, and the condition always stays true.

If you run this in DartPad, the infinite loop will cause the browser to hang. Instead use break, so that the loop will end after i reaches 5:

do {
  print(i);
  if (i == 5) {
    break;
  }
  ++i;
} while (i < 10);
// 1
// 2
// 3
// 4
// 5

Run the code now and see that the loop ends after 5 iterations.

For Loops

The loops that loop a pre-determined number of times are for loops in Dart, which once again are very similar to those from other languages.

Dart has both a C-like form of a for loop, with an initialization, loop condition, and an action, as well a for-in loop for iterating over a collection of objects. In the first form, the initialization runs before the first loop iteration. The condition is checked on entering each iteration, and the action is run before starting the next iteration.

For the for-in form, a variable is set to each element in a collection of Dart objects for each subsequent iteration of the loop. Let's say you want to sum up the values of the first 10 integers.

Create a variable for the sum:

var sum = 0;

Then use a for loop in which you initialize a loop counter i to 1, check that i is less than or equal to 10, and increment i after every loop. Inside the loop use a compound assignment to add i to the running sum:

for (var i = 1; i <= 10; i++) {
  sum += i;  
}
print("The sum is $sum"); // The sum is 55

Run your code in DartPad to see the sum.

An example of a Dart collection is a simple list of numbers made with square brackets:

var numbers = [1, 2, 3, 4];

You can use a for-in loop to iterate over the list:

for (var number in numbers) {
  print(number);
}
// 1
// 2
// 3
// 4

The variable number takes on the value of each element of numbers as you iterate in the loop.

Lists like numbers also have a function you can call on them named forEach, which simplifies the previous loop down to one line:

numbers.forEach((number) => print(number));
// 1
// 2
// 3
// 4

Here you've used an anonymous function and arrow syntax, both of which you'll see later on in the section on Functions.

Finally, like while loops, for loops can also use continue and break to control flow within the loop. For example, if you want to skip printing the number 3, you can use a continue statement in the for-in loop:

for (var number in numbers) {
  if (number == 3) {
    continue;
  }
  print(number);
}
// 1
// 2
// 4

switch and enum

Dart also has support for a switch statement and enumerations using enum. Consult the Dart documentation for more on both. Like most of the rest of the Dart constructs you've seen, they work similarly to how they work in other languages like C and Java.

Functions

Functions let you package multiple related lines of code into a single body that you can summon to avoid repeating those lines of code throughout your Dart application:

Functions

A function consists of a return type, a function name, a parameter list in parentheses, and then a function body enclosed in braces. The code you're turning into the function goes inside the braces. When you call the function, you pass in arguments that match the types of the parameters of the function.

Note: Specifying a return type on functions is optional. If you leave off the return type, values returned from the function will either have an inferred type, or be dynamic if the type cannot be inferred. That allows you to return values of different types from the function. Usually you'll want to avoid this and instead specify a single return type from functions.

Typically, functions are defined either outside other functions or inside Dart classes, but you can also nest Dart functions in one another. For example, you can add the functions in this section inside main.

Write a new function in DartPad that will just check to see if a given string is banana.

bool isBanana(String fruit) {
  return fruit == 'banana';
}

The function uses return to return a bool value determined by the argument passed to the function. This function will always return the same value for any given input. If a function does not need to return a value, you can set the return type to void, such as for the main function.

You can call the function by passing in a string. Then you might pass the result of that call onto print:

var fruit = 'apple';
print(isBanana(fruit)); // false

You can change the argument passed to functions, and call them again with a new argument:

fruit = 'banana';
print(isBanana(fruit));  // true

The result of calling the function depends entirely on the arguments passed in.

Optional Parameters

If a parameter to a function is optional, you can surround it with square brackets:

String fullName(String first, String last, [String title]) {
  return "${title == null ? "" : "$title "}$first $last";
}

If an optional parameter is not included in the function call, the value used is null for the parameter inside the function body.

Then you can call the function with or without the optional parameter:

print(fullName("Joe", "Howard"));
// Joe Howard

print(fullName("Albert", "Einstein", "Professor"));
// Professor Albert Einstein

Optional Named Arguments

With Dart functions, you can use optional named arguments by surrounding the parameter list with braces:

bool withinTolerance({int value, int min, int max}) {
  return min <= value && value <= max;
}

Then you can pass in arguments in a different order by supplying the parameter names with a colon:

print(withinTolerance(min: 1, max: 10, value: 11)); // false

Like for optional parameters, arguments with optional names do not need to be added to a function call, and the corresponding parameters will be given the value null in the function.

Default Values

You can also assign default values to one or more parameters using equals:

bool withinTolerance({int value, int min = 0, int max = 10}) {
  return min <= value && value <= max;
}

You can then leave off the parameters with default values when calling the function.

print(withinTolerance(value: 5)); // true

Run your code to see your new functions in action.

First-Class Functions

Dart has support for what are called first-class functions. The term means that functions are treated like any other data type, that is, they can be assigned to variables, passed as arguments, and returned from other functions.

You can use the Function type to specify that a parameter named op is a function itself:

int applyTo(int value, int Function(int) op) {
  return op(value);
}

So if you have a function named square:

int square(int n) {
  return n * n;
}

You can pass square to applyTo as an argument:

print(applyTo(3, square)); // 9

You can assign a function to a variable:

var op = square;

Then you can call the function on the variable as if it were an alias for the original function:

print(op(5)); // 25

You'll see more about returning functions from other functions a bit later.

Anonymous Functions and Closures

The functions you've seen so far have been named functions, functions with a name. Anonymous functions are just what you might expect, functions without a name. You can also leave off the return type which will be inferred. So you just need a parameter list and a function body for an anonymous function.

You can assign an anonymous function to a variable:

var multiply = (int a, int b) {
  return a * b;
};

The right-hand-side consists of a parameter list and a function body.

Since multiply now holds an anonymous function, you can call it as you would any other function:

print(multiply(14, 3)); // 42

You've seen a preview earlier of lists and the forEach function. forEach is a great example of using an anonymous function.

So if you have a list of numbers:

numbers = [1, 2, 3];

You can call forEach on the list and pass in an anonymous function that triples an element and prints out the tripled value:

numbers.forEach((number) { 
  var tripled = number * 3;  
  print(tripled);
  // 3
  // 6
  // 9
});

You can return an anonymous function from another function:

Function applyMultiplier(num multiplier) {
  return (num value) { 
    return value * multiplier;    
  };
}

This is useful for generating functions with a given behavior but with varying input parameter values. So the return value here is a function that takes one num parameter and multiplies it by the multiple used to generate the function.

An example is creating a variable named triple which multiplies its input by 3:

var triple = applyMultiplier(3);

You can calling triple with either type of num:

print(triple(6)); // 18
print(triple(14.0)); // 42

Anonymous functions in Dart act as closures, which means they "close-over" variables defined outside of themselves. For example, the return value of the applyMultiplier function above is a closure that has access to the multiplier variable defined elsewhere, in that case in the parameter list for applyMultiplier itself.

Arrow Syntax

When a Dart function, either named or anonymous, consists of just one line of code, you can reduce the function body using the Dart arrow syntax.

For example, multiply above becomes:

var multiply = (int a, int b) => a * b;

You've removed the braces and the return statement.

You can also use arrow syntax with applyMultiplier:

Function applyMultiplier(num multiplier) =>
  (num value) => value * multiplier;

You've used arrow syntax twice here: the first is denoting the return value for the applyMultiplier function, the second is within the return value itself, which is an anonymous function.

In the example just above using forEach, you cannot use arrow syntax, since the body of the function you're passing to forEach has more than one line of code.

Where to Go From Here?

That concludes this brief tour of the basics of Dart. You should now have a better understanding of the Dart code you see when learning how to build Flutter apps.

You can download all the code for this tutorial by clicking the Download Materials button at the top or bottom of the page. You can either paste the code in the main.dart file into DartPad or use the Dart SDK to run the file.

As mentioned in the introduction, you can go beyond the basics and learn more about Object-Oriented Programming in Dart as well as Dart collection types such as lists, sets, and maps, in our course Programming in Dart - Fundamentals, on which this short tutorial is based.

Check out the official Dart documentation to learn about intermediate and advanced parts of Dart:

  • Iterables and Generators
  • Exceptions
  • Runes and symbols
  • Asynchronous Dart with isolates, streams, futures and async/await

We'll be covering these and other Dart topics in future tutorials and video courses.

I hope you enjoyed this brief introduction to Dart basics. Please write to us in the forums below if you have questions or comments!

Average Rating

4.9/5

Add a rating for this content

16 ratings

Contributors

Comments